8. 摄像机类实现

效果,按下wasdqr可以平移,鼠标拖动可以旋转,滑轮可以缩放

 照相机类头文件

#pragma once
#include <QVector3D>
#include <QMatrix4x4>
#include <QKeyEvent>
enum Camera_Movement {
    FORWARD,//
    BACKWARD,//
    LEFT,//
    RIGHT,//
    UP,//
    DOWN//
};

const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 1.0f;
const float SENSITIVITY = 0.01f;
const float ZOOM = 45.0f;

class Camera {
public:
    //相机位置,相机的上轴,yaw关于y轴的旋转向左向右看的程度,pitch关于x轴的旋转,向上向下看的俯仰角
    Camera(QVector3D position = QVector3D(0.0f, 0.0f, 0.0f), QVector3D up = QVector3D(0.0f, 1.0f, 0.0f),
        float yaw = YAW, float pitch = PITCH);
    ~Camera();

    QMatrix4x4 getViewMatrix();
    void processMouseMovement(float xoffset, float yoffset, bool constraintPitch = true);
    void processMouseScroll(float yoffset);
    void processInput(float dt);

    QVector3D position;//摄像机位置向量
    QVector3D worldUp;//世界坐标的上轴
    QVector3D front;//摄像机的方向向量,其实就是-position

    QVector3D up;//上向量
    QVector3D right;//右向量

    //俯仰角
    float picth;
    //偏航角
    float yaw;

    float movementSpeed;//鼠标速度
    float mouseSensitivity;//鼠标灵敏度
    float zoom;////缩放 之前有一个函数projection.perspective定义了视角的缩放,在那边会用到

    
    bool keys[1024];
private:
    void updateCameraVectors();
    void processKeyboard(Camera_Movement direction, float deltaTime);
};

照相机类源文件

#include "Camera.h"
#include <QDebug>
//传参
Camera::Camera(QVector3D position, QVector3D up, float yaw, float pitch) :
    position(position),
    worldUp(up),
    front(-position),
    picth(pitch),
    yaw(yaw),
    movementSpeed(SPEED),
    mouseSensitivity(SENSITIVITY),
    zoom(ZOOM) {

    this->updateCameraVectors();

    for (uint i = 0; i != 1024; ++i)
        keys[i] = false;
}

Camera::~Camera()
{

}

// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
QMatrix4x4 Camera::getViewMatrix()
{
    QMatrix4x4 view;
    //(const QVector3D& eye, const QVector3D& center, const QVector3D& up);
    view.lookAt(this->position, this->position + this->front, this->up);
    return view;
}
//键盘按下事件
void Camera::processKeyboard(Camera_Movement direction, float deltaTime)
{
    float velocity = this->movementSpeed * deltaTime;//速度
    if (direction == FORWARD)
        this->position += this->front * velocity;
    if (direction == BACKWARD)
        this->position -= this->front * velocity;
    if (direction == LEFT)
        this->position -= this->right * velocity;
    if (direction == RIGHT)
        this->position += this->right * velocity;
    if (direction == UP)
        this->position += this->worldUp * velocity;
    if (direction == DOWN)
        this->position -= this->worldUp * velocity;
}
//鼠标移动事件
void Camera::processMouseMovement(float xoffset, float yoffset, bool constraintPitch)
{
    xoffset *= this->mouseSensitivity;
    yoffset *= this->mouseSensitivity;

    this->yaw += xoffset;
    this->picth += yoffset;

    if (constraintPitch) {
        if (this->picth > 89.0f)
            this->picth = 89.0f;
        if (this->picth < -89.0f)
            this->picth = -89.0f;
    }

    this->updateCameraVectors();
}

// 该参数表示竖直滚动大小,因为45时默认的视野值,所以把缩放级别限制在1到45
void Camera::processMouseScroll(float yoffset)
{
    //小于45°时
    if (this->zoom >= 1.0f && this->zoom <= 45.0f)
        this->zoom -= yoffset;
    if (this->zoom > 45.0f)
        this->zoom = 45.0f;
    if (this->zoom < 1.0f)
        this->zoom = 1.0f;
}
//按键移动
void Camera::processInput(float dt)
{
    if (keys[Qt::Key_W])
        processKeyboard(FORWARD, dt);
    if (keys[Qt::Key_S])
        processKeyboard(BACKWARD, dt);
    if (keys[Qt::Key_A])
        processKeyboard(LEFT, dt);
    if (keys[Qt::Key_D])
        processKeyboard(RIGHT, dt);
    if (keys[Qt::Key_E])
        processKeyboard(UP, dt);
    if (keys[Qt::Key_Q])
        processKeyboard(DOWN, dt);
}

void Camera::updateCameraVectors()
{
    ////////////////////////////////////////////转动需要
    // 通过俯仰角和偏航角得到新的xyz位置
    QVector3D front;
    front.setX(cos(this->yaw) * cos(this->picth));
    front.setY(sin(this->picth));
    front.setZ(sin(this->yaw) * cos(this->picth));
    this->front = front.normalized();
    ////////////////////////////////////////////移动需要
    //利用世界上轴和相机向量叉乘获得右轴
    //右轴等于上向量,鼠标向左的时候我们希望它向右移动,所以是front叉乘worldUp反之就反着叉乘
    this->right = QVector3D::crossProduct(this->front, this->worldUp).normalized();
    //同上
    this->up = QVector3D::crossProduct(this->right, this->front).normalized();
}

 什么地方用到了照相机类

在构造函数中初始化其指针

在PaintGL函数中

 

 在事件重载的函数中

 

 

 

OpenGL类头文件

#pragma once
#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>

#include "Camera.h"

class QtFunctionWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
    QtFunctionWidget(QWidget *parent = nullptr);
    ~QtFunctionWidget() Q_DECL_OVERRIDE;

protected:
    virtual void initializeGL() Q_DECL_OVERRIDE;
    virtual void resizeGL(int w, int h) Q_DECL_OVERRIDE;
    virtual void paintGL() Q_DECL_OVERRIDE;

    void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
    void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
    void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;

private:
    QOpenGLShaderProgram shaderProgram;
    QOpenGLBuffer vbo;
    QOpenGLVertexArrayObject vao;
    QOpenGLTexture *texture1 = nullptr;
    QOpenGLTexture *texture2 = nullptr;

    QTimer* m_pTimer = nullptr;
    int     m_nTimeValue = 0;

    // camera
    std::unique_ptr<Camera> camera;//相机指针 两个unique_ptr指针不能指向同一个对象,不能复制只能移动
    bool m_bLeftPressed;
    QPoint m_lastPos;//上次的点
};

OpenGL类源文件

#include "QtFunctionWidget.h"
#include <QDebug>
#include <QTimer>

QtFunctionWidget::QtFunctionWidget(QWidget *parent) : QOpenGLWidget(parent),
vbo(QOpenGLBuffer::VertexBuffer)
{    
    camera = std::make_unique<Camera>(QVector3D(5.0f, 0.0f, 10.0f));//创建并且返回unique_ptr对象
    m_bLeftPressed = false;
    //重绘
    m_pTimer = new QTimer(this);
    connect(m_pTimer, &QTimer::timeout, this, [=] {
        m_nTimeValue += 1;
        update();
    });
    m_pTimer->start(40);
}

QtFunctionWidget::~QtFunctionWidget() {
    //多线程调用保护
    makeCurrent();
    //glDeleteBuffers
    vbo.destroy();
    //glDeleteVertexArrays
    vao.destroy();

    delete texture1;
    delete texture2;
    //退出保护
    doneCurrent();
}

void QtFunctionWidget::initializeGL() {
    this->initializeOpenGLFunctions();

    bool success = shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, "textures.vert");
    if (!success) {
        qDebug() << "shaderProgram addShaderFromSourceFile failed!" << shaderProgram.log();
        return;
    }

    success = shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, "textures.frag");
    if (!success) {
        qDebug() << "shaderProgram addShaderFromSourceFile failed!" << shaderProgram.log();
        return;
    }

    success = shaderProgram.link();
    if (!success) {
        qDebug() << "shaderProgram link failed!" << shaderProgram.log();
    }

    //VAO,VBO data
    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,  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, 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
    };
    //绑定vao
    QOpenGLVertexArrayObject::Binder vaoBind(&vao);
    //创建vbo
    vbo.create();
    //绑定vbo
    vbo.bind();
    
    //分配数组内存空间
    vbo.allocate(vertices, sizeof(vertices));
    
    //和glVertexAttribPointer有区别
    //glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer)
    // void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0);//不需要填是否标准化
    shaderProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);//位置0,数据类型float,起始从0开始,元组大小为3个,步长为5个float
    shaderProgram.enableAttributeArray(0);
    shaderProgram.setAttributeBuffer(1, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5);
    shaderProgram.enableAttributeArray(1);

    vbo.release();//vbo操作完毕,接下来就没有对vbo内存的操作了
    //纹理一
    texture1 = new QOpenGLTexture(QImage("container.jpg"), QOpenGLTexture::GenerateMipMaps);
    if (!texture1->isCreated()) {
        qDebug() << "Failed to load texture";
    }
    //纹理的样式
    texture1->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
    texture1->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
    texture1->setMinificationFilter(QOpenGLTexture::Linear);
    texture1->setMagnificationFilter(QOpenGLTexture::Linear);

    //纹理二
    texture2 = new QOpenGLTexture(QImage("awesomeface.png").mirrored(true, true), QOpenGLTexture::GenerateMipMaps);
    if (!texture2->isCreated()) {
        qDebug() << "Failed to load texture";
    }
    
    texture2->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
    texture2->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
    
    texture2->setMinificationFilter(QOpenGLTexture::Linear);
    texture2->setMagnificationFilter(QOpenGLTexture::Linear);

    // tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
    shaderProgram.bind();//纹理的值传递给着色器
    shaderProgram.setUniformValue("texture1", 0);//0代表第一个纹理
    shaderProgram.setUniformValue("texture2", 1);//1代表第二个纹理

    
    glEnable(GL_DEPTH_TEST);//开启深度测试
}

void QtFunctionWidget::resizeGL(int w, int h) {
    glViewport(0, 0, w, h);
}
//位置数组
static 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)
};

void QtFunctionWidget::paintGL() {
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

    camera->processInput(1.0f);//本来参数是用来计算电脑两帧之间的时间的,这里用1代替

    //绑定纹理
    glActiveTexture(GL_TEXTURE0);
    texture1->bind();
    glActiveTexture(GL_TEXTURE1);
    texture2->bind();
    //想要着色器调用我们的c++程序就需要先bind
    shaderProgram.bind();

    QMatrix4x4 projection;
    //perspective的第一个参数是缩放参数
    projection.perspective(camera->zoom, 1.0f * width() / height(), 0.1f, 100.f);
    shaderProgram.setUniformValue("projection", projection);

    //通过照相机对象的内置函数获取视图数组
    shaderProgram.setUniformValue("view", camera->getViewMatrix());

    {//绑定vao
        QOpenGLVertexArrayObject::Binder vaoBind(&vao);

        for (unsigned int i = 0; i < 10; i++) {
            // 对每个物体都要计算model矩阵
            QMatrix4x4 model;
            model.translate(cubePositions[i]);//平移
            float angle = (i + 1.0f) * m_nTimeValue;
            model.rotate(angle, QVector3D(1.0f, 0.3f, 0.5f));//旋转
            shaderProgram.setUniformValue("model", model);//把数据传递给着色器
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
    }
    //解除绑定
    texture1->release();
    texture2->release();
    shaderProgram.release();
}
//键盘按下事件
//keys是一个bool的数组,通过以下两个函数来判断键盘是否按下
void QtFunctionWidget::keyPressEvent(QKeyEvent *event)
{
    
    int key = event->key();
    
    if (key >= 0 && key < 1024)
        camera->keys[key] = true;
}
void QtFunctionWidget::keyReleaseEvent(QKeyEvent *event)
{
    int key = event->key();
    if (key >= 0 && key < 1024)
        camera->keys[key] = false;
}
//鼠标是否按下事件
void QtFunctionWidget::mousePressEvent(QMouseEvent *event)
{
    //按下该键把参数设置成true
    if (event->button() == Qt::LeftButton) {
        m_bLeftPressed = true;
        m_lastPos = event->pos();
    }
}
//鼠标释放函数,把该值设置成false
void QtFunctionWidget::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event);//Q_UNUSED() 没有实质性的作用,用来避免编译器警告
    m_bLeftPressed = false;
}
//鼠标移动事件
void QtFunctionWidget::mouseMoveEvent(QMouseEvent *event)
{
    //如果鼠标按下了就触发鼠标移动事件
    if (m_bLeftPressed) {
        int xpos = event->pos().x();
        int ypos = event->pos().y();
        //x,y移动的距离
        int xoffset = xpos - m_lastPos.x();
        int yoffset = m_lastPos.y() - ypos;//qt的y坐标往下是正,opengl y坐标往上是正,openGL远点在中间,QT在左上角
        //重置上一次的移动位置
        m_lastPos = event->pos();
        camera->processMouseMovement(xoffset, yoffset);
    }
}
//滚轮事件
void QtFunctionWidget::wheelEvent(QWheelEvent *event)
{
    QPoint offset = event->angleDelta();
    camera->processMouseScroll(offset.y() / 20.0f);
}

顶点着色器

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

片段着色器

#version 330 core
out vec4 FragColor;
 
in vec2 TexCoord;
 
uniform sampler2D texture1;
uniform sampler2D texture2;
 
void main()
{
    FragColor = mix(texture2D(texture1, TexCoord), texture2D(texture2, TexCoord), 0.2f);
}

 

posted @ 2021-07-13 10:41  一拳超人的逆袭  阅读(100)  评论(0编辑  收藏  举报