当前位置: 首页 > article >正文

qt+opengl 实现一个3D立体体

qt+opengl 实现纹理贴图,平移旋转,绘制三角形,方形-CSDN博客

QT + opengl 让2d贴图动起来-CSDN博客

在前两个文章中我们已经了解了opengl的基本用法和GLSL的语句的基本意思,和纹理位置颜色的坐标位置关系。那么我们现在从2d图形来完成一个3d立方体。

1 首先我们理解几个名词 模型变化,视点变化,从三维物体到二维图象,就如同用相机拍照一样

  1.1  、将相机置于三角架上,让它对准三维景物,它相当于OpenGL中调整视点的位置,即视点变换(Viewing Transformation)。

 1.2  将三维物体放在场景中的适当位置,它相当于OpenGL中的模型变换(Modeling Transformation),即对模型进行旋转、平移和缩放。

1.3 选择相机镜头并调焦,使三维物体投影在二维胶片上,它相当于OpenGL中把三维模型投影到二维屏幕上的过程,即OpenGL的投影变换(Projection Transformation),OpenGL中投影的方法有两种,即正射投影和透视投影。为了使显示的物体能以合适的位置、大小和方向显示出来,必须要通过投影。有时为了突出图形的一部分,只把图形的某一部分显示出来,这时可以定义一个三维视景体(Viewing Volume)。正射投影时一般是一个长方体的视景体,透视投影时一般是一个棱台似的视景体。只有视景体内的物体能被投影在显示平面上,其他部分则不能。

1.4 冲洗底片,决定二维相片的大小,它相当与OpenGL中的视口变换(Viewport Transformation)(在屏幕窗口内可以定义一个矩形,称为视口(Viewport),视景体投影后的图形就在视口内显示)规定屏幕上显示场景的范围和尺寸。

1.5 投影变换  投影变换的目的就是定义一个视景体,使得视景体外多余的部分裁剪掉,最终进入图像的只是视景体内的有关部分。投影包括透视投影(Perspective Projection)和正视投影(Orthographic Projection)两种。

我们用大白话来说明下:

在用相机拍摄物体时,我们可以保持物体的位置不动,而将相机移离物体,这就相当于视点变换;另外,我们也可以保持相机的固定位置,将物体移离相机,这就相当于模型转换。这样,在OpenGL中,以逆时针旋转物体就相当于以顺时针旋转相机。因此,我们必须把视点转换和模型转换结合在一起考虑,而对这两种转换单独进行考虑是毫无意义的。

下面我们在前两篇文章的基础上再来做出一个立方体,上代码:


#include "glwidget.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>

static const char *vertexShaderSource =
    "#version 330\n"
    "layout (location = 0) in vec4 vertex;\n"
    "layout (location = 1) in vec4 texCoord;\n"
    "out vec4 texc;\n"
    "uniform  mat4 matrix;\n"
    "void main(void)\n"
    "{\n"
    "    gl_Position = matrix * vertex;\n"
    "    texc = texCoord;\n"
    "}\n";

static const char *fragmentShaderSource =
      "#version 330\n"
      "uniform sampler2D texture;\n"
      "in vec4 texc;\n"
      "void main(void)\n"
      "{\n"
      "    gl_FragColor = texture2D(texture, texc.st);\n"
      "}\n";

GLWidget::GLWidget():QOpenGLWidget()
  ,m_xRos(0)
  ,m_yRos(0)
{
    timer = new QTimer;
    timer->setInterval(20);
    connect(timer,&QTimer::timeout,this,[=]{
        qDebug()<<"timeout";
//       m_xRos+=30;
//       m_yRos+=30;
       rotateBy(2 * 16, +2 * 16, -1 * 16);
    });
    timer->start();
}
GLWidget::~GLWidget()
{
    makeCurrent();
    vbo.destroy();
    for (int i = 0; i < 6; ++i)
        delete textures[i];
    delete program;
    doneCurrent();
}

QSize GLWidget::minimumSizeHint() const
{
    return QSize(400, 400);
}

QSize GLWidget::sizeHint() const
{
    return QSize(400, 400);
}

void GLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
{
    xRot += xAngle;
    yRot += yAngle;
    zRot += zAngle;
    update();
}

void GLWidget::setClearColor(const QColor &color)
{
    clearColor = color;
    update();
}

void GLWidget::initializeGL()
{
    vertices = {
            // ---- 位置----   - 纹理坐标 -      ---- 颜色 ----

        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,//1

        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,//2


        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,//3



        -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,//4


         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,//5

        -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,

    };
    initializeOpenGLFunctions();

   // makeObject();

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

//#define PROGRAM_VERTEX_ATTRIBUTE 0
//#define PROGRAM_TEXCOORD_ATTRIBUTE 1

    program = new QOpenGLShaderProgram;
    program->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);
    program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSource);
    program->link();
    program->bind();//激活Program对象

    vbo.create();
    vbo.bind();              //绑定到当前的OpenGL上下文,
    vbo.allocate(vertices.constData(), vertices.count() * sizeof(GLfloat));


    //初始化VAO,设置顶点数据状态(顶点,法线,纹理坐标等)
    vao.create();
    vao.bind();

    for (int j = 0; j < 6; ++j)
    {
        textures[j] = new QOpenGLTexture(QImage(QString(":/%1.png").arg(j + 1)).mirrored());
        textures[j]->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
        textures[j]->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);
        textures[j]->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge);
    }
    program->setAttributeBuffer(0, GL_FLOAT, 0,                  3, 5 * sizeof(float));   //设置aPos顶点属性
    program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float),  2, 5 * sizeof(float));   //设置aColor顶点颜色
    program->enableAttributeArray(0); //使能aPos顶点属性
    program->enableAttributeArray(1); //使能aColor顶点颜色
    program->setUniformValue("texture", 0);
//    vao.release();
//    vbo.release();
}

void GLWidget::paintGL()
{
    //glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
    glClearColor(0.1f,0.5f,0.7f,1.0f);  //设置清屏颜色
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 m;
    //m.ortho(-0.5f, +0.5f, +0.5f, -0.5f, 4.0f, 15.0f);
    m.translate(0.0f, 0.0f, 0.0f);
    m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
    m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
    m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
    m.scale(0.5);
    program->setUniformValue("matrix", m);

    for (int i = 0; i < 6; ++i) {
        textures[i]->bind();
        glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
    }
}
void GLWidget::resizeGL(int width, int height)
{
    this->glViewport(0,0,width,height);                //定义视口区域
}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    int dx = event->x() - lastPos.x();
    int dy = event->y() - lastPos.y();

    if (event->buttons() & Qt::LeftButton) {
        rotateBy(8 * dy, 8 * dx, 0);
    } else if (event->buttons() & Qt::RightButton) {
        rotateBy(8 * dy, 0, 8 * dx);
    }
    lastPos = event->pos();
}

void GLWidget::mouseReleaseEvent(QMouseEvent * /* event */)
{
    emit clicked();
}
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QTimer>

QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)

class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    //using QOpenGLWidget::QOpenGLWidget;
    GLWidget();
    ~GLWidget();

    QSize minimumSizeHint() const override;
    QSize sizeHint() const override;
    void rotateBy(int xAngle, int yAngle, int zAngle);
    void setClearColor(const QColor &color);

signals:
    void clicked();

protected:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int width, int height) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:

    QColor clearColor = Qt::black;
    QPoint lastPos;
    int xRot = 0;
    int yRot = 0;
    int zRot = 0;
    QOpenGLShaderProgram *program = nullptr;
    QOpenGLBuffer vbo;
    QVector<float> vertices;
    QOpenGLVertexArrayObject vao;
    QTimer* timer;
    int m_xRos = 0;
    int m_yRos = 0;
};

#endif

运行看看看,是不是就出来一个会动的立方体呢?

和前两篇文章的区别在于,我们贴了6张纹理图,设置了旋转角度。在测试过程里面,有几次我贴图完后,运行出来显示出来少了两个贴图,最后检查是顶点坐标错误造成的。

完整代码在

https://download.csdn.net/download/foxgod/89899780

1 widget w ;w.show 运行出来是一个三角形

2 GLWidget w ;w.show 运行出来是一个立方体

有疑问可以私信


http://www.kler.cn/news/355480.html

相关文章:

  • [LeetCode 题3] 没有重复字符的最长的子字符串
  • Excel制作工资表
  • Server-Sent Event(SSE) GPT场景实现
  • Python脚本实现发送QQ邮件
  • RabbitMQ 入门(八)SpringAMQP消息转换器
  • jmeter中对于有中文内容的csv文件怎么保存
  • C语言复习第4章 数组
  • webm格式怎么转换成mp4?几个操作简单的视频格式转换方法
  • 华为OD机试真题---勾股数元组
  • css 如何根据子元素给他的父元素设置样式
  • 【Vue】Vue3.0(十一)Vue 3.0 中 computed 计算属性概念、使用及示例
  • 还在滥用模糊查找?这类场景下 MySQL 多值索引性能更加强悍!
  • Leetcode|209.长度最小的子数组 And 59.螺旋矩阵||
  • 【C++】哈希表的封装——同时实现unordered_map和unordered_set
  • 【Vue】Vue3.0 (十二)、watchEffect 和watch的区别及使用
  • 【电商项目】1分布式基础篇
  • ASPICE在国内应用的挑战与改进空间
  • 奥比中光opencv显示可见光图片
  • [论文笔记] llama-factory 微调qwen2.5、llama3踩坑
  • php strtr 函数的坑