opengl 投影、摄像机、矩形转换与纹理,在Qt中实现

          3维空间的物体比2维更真实,更酷炫;其变换大家都知道是通过矩阵转换来的;可具体到底是怎样转换的呢,网上大多都帖的一些理论,本文给出一个示例供大家学习。

        本示例在Qt中显示,可以接收用户输入。同样也是开发opengl程序,步奏当然一样的。我这里将渲染器分离了出来以便能复用

在Qt中的显示我使用的是QOpenglWidget。

     显示部分申明如下

#ifndef WIDGET_H
#define WIDGET_H

#include <QOpenGLWidget>
#include "matrixshader.h"
#include <QTimer>
class Widget : public QOpenGLWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

protected:
    void resizeGL(int w, int h) override; //当窗口变化时的回调
    void initializeGL() override; //初始化回调
    void paintGL() override; //绘画回调
    void wheelEvent(QWheelEvent *event) override; //鼠标滚轮回调,用于输入

private:
    MatrixShader m_matxShader; //定义一个写好的渲染器
    QMatrix4x4 m_proMM; //投影矩阵
    QVector3D m_eye,m_target; //摄像机位置
    float m_angle = 0; //水平绕y轴旋转角度
    float m_curX = 0,m_curY = 0,m_curZ = 0; //物体自身3个轴向旋转角度
    float m_ez = 5; //摄像机位置的初始z值
    QTimer m_tm; //闹钟用于触发刷新

private slots:
    void slotTimeout();
};

#endif // WIDGET_H

实现部分如下

#include "widget.h"
#include <QWheelEvent>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent),
      m_target(0,0,-1)
{
    m_tm.setInterval(60);
    connect(&m_tm,SIGNAL(timeout()),this,SLOT(slotTimeout()));
    m_tm.start();
}

Widget::~Widget()
{

}

void Widget::resizeGL(int w, int h)
{
    m_proMM.setToIdentity();
    m_proMM.perspective(60.0f,GLfloat(w)/h,0.01f,100.0f); //透视宽高比和窗体宽高比一样,可以保证物体不变形
}

void Widget::initializeGL()
{
    m_matxShader.initialize(); //渲染器初始化
}

void Widget::paintGL()
{
    QMatrix4x4 mvpM; //变换矩阵
    mvpM.rotate(30,1,0,0); //先绕x轴旋转30度
    mvpM.rotate(m_angle,0,1,0); //再绕y轴旋转m_angle角度
    mvpM.translate(4,0,0); //再平移沿x轴向右平移4
    mvpM.rotate(m_curX,1,0,0); //自身绕x轴旋转m_curX角度
    mvpM.rotate(m_curY,0,1,0); //自身绕y轴旋转m_curY角度
    mvpM.rotate(m_curZ,0,0,1); //自身绕z轴旋转m_curZ角度

    QMatrix4x4 camera;
    m_eye.setZ(m_ez);
    camera.lookAt(m_eye,m_eye + m_target,QVector3D(0,1,0)); //设置摄像机位置为垂直向里看,头顶方向垂直向上
    m_matxShader.render(QOpenGLContext::currentContext()->extraFunctions(),m_proMM,camera,mvpM); //渲染
}

void Widget::slotTimeout()
{
    //每过一定时间,增加一定值
    m_angle += 5; 
    m_curX += 5;
    m_curY += 5;
    m_curZ += 5;
    update();
}

void Widget::wheelEvent(QWheelEvent *event)
{
//调整摄像位置
    if (! event->pixelDelta().isNull()) {
        m_ez += event->pixelDelta().y();
    } else if (!event->angleDelta().isNull()) {
        m_ez += (event->angleDelta() / 120).y();
    }

    event->accept();
    update();
}

渲染器申明如下

#ifndef MATRIXSHADER_H
#define MATRIXSHADER_H

#include <QOpenGLShaderProgram>
#include <QOpenGLExtraFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
class MatrixShader
{
public:
    MatrixShader() = default;
    void initialize();
    void render(QOpenGLExtraFunctions *f,QMatrix4x4 &projM,QMatrix4x4 &came,QMatrix4x4 &mvp);

private:
    QOpenGLShaderProgram m_program;
    QOpenGLBuffer m_vbo;
    QOpenGLTexture *m_texs[6]; //正文体的6个纹理
};

#endif // MATRIXSHADER_H

实现如下

#include "matrixshader.h"

void MatrixShader::initialize()
{
//编译链接shader
    m_program.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,"vsrc.vsh");
    m_program.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,"fsrc.fsh");
    m_program.link();
//生成顶点数据和纹理
    const GLfloat coords[6][8][3] = {
        { { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } },
        { { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } },
        { { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } },
        { { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } },
        { { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } },
        { { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } }
    };
    for(int i = 0; i < 6; i++){
        m_texs[i] = new QOpenGLTexture(QImage(QString("images/side%1.png").arg(i + 1)).mirrored());
    }
    QVector<GLfloat> vertData;
    for (int i = 0; i < 6; ++i) {
        for (int j = 0; j < 4; ++j) {
            // vertex position
            vertData.append(coords[i][j][0]);
            vertData.append(coords[i][j][1]);
            vertData.append(coords[i][j][2]);
            // texture coordinate
            vertData.append(j == 0 || j == 3);
            vertData.append(j == 0 || j == 1);
        }
    }
//在opengl server端开劈数据缓冲区
    m_vbo.create();
    m_vbo.bind();
    m_vbo.allocate(vertData.constData(),vertData.count() * sizeof(GLfloat));
}

void MatrixShader::render(QOpenGLExtraFunctions *f, QMatrix4x4 &projM, QMatrix4x4 &came, QMatrix4x4 &mvp)
{
    f->glClearColor(0.0, 0.0, 0.0, 0.0);
    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    f->glEnable(GL_DEPTH_TEST);
    f->glEnable(GL_CULL_FACE);

    m_program.bind(); //使用m_program的一套渲染器
    m_vbo.bind(); //将开避在opengl server端的内存绑定过来 以便于渲染使用
    f->glActiveTexture(GL_TEXTURE0 + 2); //激活2号纹理,用于渲染使用
    m_program.setUniformValue("uProjMatrix",projM);
    m_program.setUniformValue("camMatrix",came);
    m_program.setUniformValue("uMVPMatrix",mvp);
    m_program.setUniformValue("uTexture",2); //指明栅格化着色器中的纹理单元为激活的2号
    m_program.enableAttributeArray(0); //使能顶点属性
    m_program.enableAttributeArray(1); //使能纹理属性
    m_program.setAttributeBuffer(0,GL_FLOAT,0,3,5*sizeof(GLfloat)); //设置顶点数据
    m_program.setAttributeBuffer(1,GL_FLOAT,3 * sizeof(GLfloat),2,5 * sizeof(GLfloat)); //设置纹理数据

    for(int i = 0; i < 6; i++){
        m_texs[i]->bind(2); //将纹理数据绑定到激活的2号纹理单元,将纹理数据传递到了激活的实际的纹理单元上
        f->glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
    }

    m_program.disableAttributeArray(0);
    m_program.disableAttributeArray(1);
    m_vbo.release();
    m_program.release();

    f->glDisable(GL_DEPTH_TEST);
    f->glDisable(GL_CULL_FACE);
}

顶点着色器如下

#version 330
uniform mat4 uMVPMatrix;
uniform mat4 uProjMatrix;
uniform mat4 camMatrix;
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec2 aTexture;
smooth out vec4 vColor;

void main(void)
{
    gl_Position = uProjMatrix * camMatrix * uMVPMatrix * vec4(aPosition,1); //注意顺序,先乘的后作用
    vColor = vec4(aTexture,1,1);
}

栅格化着色器如下

#version 330
uniform sampler2D uTexture;
smooth in vec4 vColor;
out vec4 fragColor;

void main(void)
{
    fragColor = texture2D(uTexture,vColor.rg);
}

运行效果如下:

 1、需要显示支持opengl 3.3 compatibility context

 2、可以用鼠标控制远近

 3、我的设计是先绕x轴旋转固定的30度,这样看起来就是倾斜的;再绕y轴旋转m_angle角度(每过一定时间递增);再向x轴正方向平移4,这样就会在一个倾斜的平面绕y轴旋转了;然后在平移后加入自身旋转就达到上面的效果了;关于纹理这里就不多说了

本项目已在gitlab上,有兴趣的朋友可以下载;不要忘了加star哦。https://gitlab.com/gitHubwhl562916378/matrix

猜你喜欢

转载自blog.csdn.net/wanghualin033/article/details/82528994