QtOpenGL开发(二)绘制立方体


在OpenGL中三维实体会被拆分成一个个小的平面图形来进行绘制。比如我们绘制的立方体,可以拆分成六个平面来进行绘制。这里我们以一个带纹理的正方体来说明一下三维实体的绘制方法。

绘制立方体

由于正方体的点比较多,为了提升数据的封装性我们定义一个结构体来表示每个点对应的空间坐标和纹理坐标。

struct CubePoint {
        QVector3D posCoord;        //空间坐标
        QVector2D textureCoord;    //纹理坐标
        CubePoint(const QVector3D& position = QVector3D(),
                      const QVector2D& texture = QVector2D()):
            posCoord(position), textureCoord(texture) {
        }
    };

添加了立方体每个点的数据结构之后,我们就可以添加对应的坐标信息了。立方体的坐标示意图如下所示:
在这里插入图片描述

在程序中初始化各个顶点的位置信息和纹理信息:

//QVector<CubePoint> m_data;
//m_data是顶点信息的数据容器
void SimpleGLWindow::initializeCubeData()
{
    //正方体六个面对应的数据
    m_data = {
        // FRONT FACE
        {{-0.5, -0.5,  0.5}, {0, 0}},        {{ 0.5, -0.5,  0.5}, {1, 0}},
        {{ 0.5,  0.5,  0.5}, {1, 1}},        {{-0.5,  0.5,  0.5}, {0, 1}},

        // TOP FACE
        {{-0.5,  0.5,  0.5}, {0, 0}},        {{ 0.5,  0.5,  0.5}, {1, 0}},
        {{ 0.5,  0.5, -0.5}, {1, 1}},        {{-0.5,  0.5, -0.5}, {0, 1}},

        // BACK FACE
        {{-0.5,  0.5, -0.5}, {0, 0}},        {{ 0.5,  0.5, -0.5}, {1, 0}},
        {{ 0.5, -0.5, -0.5}, {1, 1}},        {{-0.5, -0.5, -0.5}, {0, 1}},

         // BOTTOM FACE
        {{-0.5, -0.5, -0.5}, {0, 0}},        {{ 0.5, -0.5, -0.5}, {1, 0}},
        {{ 0.5, -0.5,  0.5}, {1, 1}},        {{-0.5, -0.5,  0.5}, {0, 1}},

        // LEFT FACE
        {{-0.5, -0.5, -0.5}, {0, 0}},        {{-0.5, -0.5,  0.5}, {1, 0}},
        {{-0.5,  0.5,  0.5}, {1, 1}},        {{-0.5,  0.5, -0.5}, {0, 1}},

        // RIGHT FACE
        {{ 0.5, -0.5,  0.5}, {0, 0}},        {{ 0.5, -0.5, -0.5}, {1, 0}},
        {{ 0.5,  0.5, -0.5}, {1, 1}},        {{ 0.5,  0.5,  0.5}, {0, 1}},
    };
}

在initializeGL()中调用数据初始化方法:

void SimpleGLWindow::initializeGL()
{
    ...
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    //开启剔除操作效果
    glEnable(GL_CULL_FACE);
    //禁用背面的的光照和阴影效果,避免不必要的计算
    glCullFace(GL_BACK);
    //初始化数据
    initializeCubeData();
    //设置刷新显示的时候的默认颜色为RGB(255,255,255)
    glClearColor(1, 1, 1, 0);
}

数据初始化完成之后,我们就可以在绘制方法中绘制立方体了,对应的绘制操作如下所示:

void SimpleGLWindow::paintGL()
{
    //清空颜色缓存和深度缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //将OpenGL视口和窗口保持相同
    glViewport(0, 0, width(), height());

    //重置变换矩阵
    glLoadIdentity();

    //为了查看方便对视图进行旋转
    //m_angle是旋转的角度
    glRotatef(m_angle, 1.0, 0.0, 0.0);
    glRotatef(m_angle, 0.0, 1.0, 0.0);

    //绑定纹理
    if(m_texture)
    {
        m_texture->bind();
    }
    
    //启动纹理
    glEnable(GL_TEXTURE_2D);
    //绘制立方体
    glBegin(GL_QUADS);
    for(const CubePoint &point: m_data) {
        glTexCoord2d(point.textureCoord.x(), point.textureCoord.y());
        glVertex3f(point.posCoord.x(), point.posCoord.y(), point.posCoord.z());
    }
    glEnd();
    //关闭纹理
    glDisable(GL_TEXTURE_2D);
}

绘制出来的效果如下所示:

在这里插入图片描述

立方体动画

上面方法绘制出来的立方体是静态的。为了让图形不那么单调,我们加一个定时器让立方体旋转起来,对应的实现如下所示:

SimpleGLWindow::SimpleGLWindow(QWindow *parent) :
    QOpenGLWindow(NoPartialUpdate, parent)
{
    //设置多采样的值
    QSurfaceFormat fmt = format();
    fmt.setSamples(25);
    setFormat(fmt);
    
    //设置定时器修改旋转的值
    QTimer* timer = new QTimer(this);
    timer->setInterval(30);
    connect(timer,&QTimer::timeout,this,[&](){m_angle+=1;this->update();});
    timer->start();
}

显示效果如下所示:
在这里插入图片描述

除了定时器外,我们还可以使用灵活度更高的动画框架来让立方体动起来,对应的设置如下:

//simpleglwindow.h
class SimpleGLWindow : public QOpenGLWindow, protected QOpenGLFunctions_1_1
{
    Q_OBJECT
    Q_PROPERTY(float angle READ angle WRITE setAngle)
public:
    qreal angle() const;
    void setAngle(const qreal &angle);
private:
     qreal m_angle = 60;
    ...
};   
//simpleglwindow.cpp
SimpleGLWindow::SimpleGLWindow(QWindow *parent) :
    QOpenGLWindow(NoPartialUpdate, parent)
{
    //设置多采样的值
    QSurfaceFormat fmt = format();
    fmt.setSamples(25);
    setFormat(fmt);

    QPropertyAnimation *animation = new QPropertyAnimation(this);
    animation->setTargetObject(this);
    animation->setPropertyName("angle");
    animation->setStartValue(0);
    animation->setEndValue(359);
    animation->setDuration(5000);
    animation->setLoopCount(-1);
    animation->setEasingCurve(QEasingCurve::InCubic);
    animation->start();
}  
qreal SimpleGLWindow::angle() const
{
    return m_angle;
}

void SimpleGLWindow::setAngle(const qreal &angle)
{
    m_angle = angle;
    this->update();
}

显示效果如下:
在这里插入图片描述

完整的工程代码,欢迎参考:

//simpleglwindow.h
#pragma execution_character_set("utf-8")

#ifndef SIMPLEGLWINDOW_H
#define SIMPLEGLWINDOW_H

#include <QOpenGLWindow>
#include <QOpenGLFunctions_1_1>
#include <QOpenGLTexture>
#include <QVector3D>
#include <QVector2D>
#include <QTimer>
#include <QObject>

//使用的OpenGLFunction的版本是1.1
class SimpleGLWindow : public QOpenGLWindow, protected QOpenGLFunctions_1_1
{
    Q_OBJECT
    Q_PROPERTY(float angle READ angle WRITE setAngle)
public:
    SimpleGLWindow(QWindow *parent = 0);

    qreal angle() const;
    void setAngle(const qreal &angle);

protected:
    //初始化操作
    void initializeGL();

    //绘制函数
    void paintGL();

protected:
    //窗口尺寸变化的事件
    void resizeGL(int w, int h);

private:
    void initialTexture();
    //初始化正方体的数据
    void initializeCubeData();
    //OpenGL纹理
    QOpenGLTexture* m_texture = nullptr;
    //纹理图片
    QImage* m_texture_image = nullptr;

    struct CubePoint {
        QVector3D posCoord;        //空间坐标
        QVector2D textureCoord;    //纹理坐标
        CubePoint(const QVector3D& position = QVector3D(),
                      const QVector2D& texture = QVector2D()):
            posCoord(position), textureCoord(texture) {
        }
    };
    qreal m_angle = 60;
    QVector<CubePoint> m_data;
};

#endif // SIMPLEGLWINDOW_H

//simpleglwindow.cpp
#include "simpleglwindow.h"
#include <QDebug>
#include <QPropertyAnimation>

SimpleGLWindow::SimpleGLWindow(QWindow *parent) :
    QOpenGLWindow(NoPartialUpdate, parent)
{
    //设置多采样的值
    QSurfaceFormat fmt = format();
    fmt.setSamples(25);
    setFormat(fmt);

    QPropertyAnimation *animation = new QPropertyAnimation(this);
    animation->setTargetObject(this);
    animation->setPropertyName("angle");
    animation->setStartValue(0);
    animation->setEndValue(359);
    animation->setDuration(5000);
    animation->setLoopCount(-1);
    animation->setEasingCurve(QEasingCurve::InCubic);
    animation->start();

//    QTimer* timer = new QTimer(this);
//    timer->setInterval(30);
//    connect(timer,&QTimer::timeout,this,[&](){m_angle+=1;this->update();});
//    timer->start();
}

void SimpleGLWindow::initializeGL()
{
    //初始化OpenGL函数
    if (!initializeOpenGLFunctions())
    {
        qDebug() << "init opengl functions failed";
    }
    initialTexture();
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    //开启剔除操作效果
    glEnable(GL_CULL_FACE);
    //禁用背面的的光照和阴影效果,避免不必要的计算
    glCullFace(GL_BACK);

    initializeCubeData();
    //设置刷新显示的时候的默认颜色为RGB(255,255,255)
    glClearColor(1, 1, 1, 0);
}

void SimpleGLWindow::paintGL()
{
    //清空颜色缓存和深度缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //将OpenGL视口和窗口保持相同
    glViewport(0, 0, width(), height());

    //重置变换矩阵
    glLoadIdentity();

    //为了查看方便对视图进行旋转
    glRotatef(m_angle, 1.0, 0.0, 0.0);
    glRotatef(m_angle, 0.0, 1.0, 0.0);

    //绑定纹理
    if(m_texture)
    {
        m_texture->bind();
    }

    //启动纹理
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
    for(const CubePoint &point: m_data) {
        glTexCoord2d(point.textureCoord.x(), point.textureCoord.y());
        glVertex3f(point.posCoord.x(), point.posCoord.y(), point.posCoord.z());
    }
    glEnd();
    //关闭纹理
    glDisable(GL_TEXTURE_2D);
}

void SimpleGLWindow::resizeGL(int w, int h)
{
    qDebug() << w << h;
}

void SimpleGLWindow::initialTexture()
{
    //初始化图片和纹理
    m_texture_image = new QImage(":/background.jpg");
    m_texture = new QOpenGLTexture(m_texture_image->mirrored());

    //添加放大和缩小的时候的滤波器
    m_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
    m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
}

void SimpleGLWindow::initializeCubeData()
{
    m_data = {
        // FRONT FACE
        {{-0.5, -0.5,  0.5}, {0, 0}},        {{ 0.5, -0.5,  0.5}, {1, 0}},
        {{ 0.5,  0.5,  0.5}, {1, 1}},        {{-0.5,  0.5,  0.5}, {0, 1}},

        // TOP FACE
        {{-0.5,  0.5,  0.5}, {0, 0}},        {{ 0.5,  0.5,  0.5}, {1, 0}},
        {{ 0.5,  0.5, -0.5}, {1, 1}},        {{-0.5,  0.5, -0.5}, {0, 1}},

        // BACK FACE
        {{-0.5,  0.5, -0.5}, {0, 0}},        {{ 0.5,  0.5, -0.5}, {1, 0}},
        {{ 0.5, -0.5, -0.5}, {1, 1}},        {{-0.5, -0.5, -0.5}, {0, 1}},

         // BOTTOM FACE
        {{-0.5, -0.5, -0.5}, {0, 0}},        {{ 0.5, -0.5, -0.5}, {1, 0}},
        {{ 0.5, -0.5,  0.5}, {1, 1}},        {{-0.5, -0.5,  0.5}, {0, 1}},

        // LEFT FACE
        {{-0.5, -0.5, -0.5}, {0, 0}},        {{-0.5, -0.5,  0.5}, {1, 0}},
        {{-0.5,  0.5,  0.5}, {1, 1}},        {{-0.5,  0.5, -0.5}, {0, 1}},

        // RIGHT FACE
        {{ 0.5, -0.5,  0.5}, {0, 0}},        {{ 0.5, -0.5, -0.5}, {1, 0}},
        {{ 0.5,  0.5, -0.5}, {1, 1}},        {{ 0.5,  0.5,  0.5}, {0, 1}},
    };
}

qreal SimpleGLWindow::angle() const
{
    return m_angle;
}

void SimpleGLWindow::setAngle(const qreal &angle)
{
    m_angle = angle;
    this->update();
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值