前言:
本文的代码是 LearnOpenGL 中对应代码,这里提供学习,大家喜欢的可去官方网站去看看: LearnOpenGL-CNhttps://learnopengl-cn.readthedocs.io/zh/latest/
本章将要讲解摄像机的功效。
Camera
? ? ?OpenGL本身没有摄像机的概念, 但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机, 这样感觉就像我们在移动, 而不是场景在移动。
如下图: ?
? ?定义一个摄像机, 我们需要一个摄像机在世界空间中的位置、 观察的方向、 一个指向它的右测的向量以及一个指向它上方的向量。 细心的读者可能已经注意到我们实际上创建了一个三个单位轴相互垂直的、 以摄像机的位置为原点的坐标系。
摄像机的一个坐标。
摄像机点? cameraPos(0,0,2); 图1
目标? ? ?cameraTarget(0,0,0) 就是我们物体的位置
摄像机方向? ? cameraDirection =?normalize(cameraPos - cameraTarget);
这样我们就得到 摄像机的一个单位向量。 图2
同理我们想要建立对应的坐标,就是如下方案:
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
//Direction
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);
//right
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
//up
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);
这里是glm定义的方式,qt中使用 QVector3D 方式来代替,这里对应写一下就好。
Look At
? ? ? ? 使用矩阵的好处之一是如果你定义了一个坐标空间, 里面有3个相互垂直的轴, 你可以用这三个轴外加一个平移向量来创建一个矩阵, 你可以用这个矩阵乘以任何向量来变换到那个坐标 空间。 这正是LookAt矩阵所做的, 现在我们有了3个相互垂直的轴和一个定义摄像机空间的位 置坐标, 我们可以创建我们自己的LookAt矩阵了:
?这里可以看出来是原来物体移动的矩阵的逆。也就是对应摄像机的位移。
基本使用方案:
QMatrix4x4 view; //观察矩阵,后退一点
float radius = 12.5;
float secs = time.elapsed()/1000.0;
float camX = sin(secs) * radius;
float camY = cos(secs) * radius;
view.lookAt(QVector3D(camX,0,camY), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
这里很好理解,radius 就是圆的半径,?camX、camY 分别是圆上的坐标。
camX^2 +?camY^2 =?radius^2? ? 这样的圆公式就懂了吧。
案例效果
?这样我们就是相当于摄像机在移动的效果。
?代码:
顶点着色器
#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.0);
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
?片段着色器
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
// linearly interpolate between both textures (80% container, 20% awesomeface)
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
//FragColor =texture(texture2,TexCoord);
}
?头文件:
#ifndef BKQOPENGLW_H
#define BKQOPENGLW_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QTimer>
#include <QTime>
class BKQOpenglW : public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
enum Shape{None,Rect,circle,Triangle};
explicit BKQOpenglW(QWidget *parent = nullptr);
~BKQOpenglW();
void drawShapes(Shape shape);
void setWireFrame(bool b);
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
signals:
public slots:
private:
unsigned int VBO, VAO,EBO;
Shape m_Shape;
QOpenGLShaderProgram shaderProgram;
unsigned int texture;
QOpenGLTexture *pTexture;
QOpenGLTexture *pTexture2;
QTimer timer;
int rotate{ 0 };
QTime time;
};
#endif // BKQOPENGLW_H
cpp
#include "bkqopenglw.h"
#include<iostream>
#include <QDebug>
#include <QSurfaceFormat>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#if 0
float vertices[] = {
// positions // colors // texture coords
0.8f, 0.8f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, // top right
0.8f, -0.8f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, // bottom right
-0.8f, -0.8f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.8f, 0.8f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 2.0f // top left
};
#endif
//顶点数据(盒子六个面,一个面两个三角)
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
};
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
};
//绘制多个盒子
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)
};
BKQOpenglW::BKQOpenglW(QWidget *parent) : QOpenGLWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
// QSurfaceFormat fmt = format();
// fmt.setRenderableType(QSurfaceFormat::OpenGL);
// fmt.setProfile(QSurfaceFormat::CoreProfile);
fmt.setVersion(3, 3);
// //当启用多重采样时,将每个像素的首选样本数设置为numSamples
// fmt.setSamples(4);
// setFormat(fmt);
connect(&timer,&QTimer::timeout,this,[this](){
rotate+=2;
if(isVisible()){
update();
}
});
timer.setInterval(50);
}
BKQOpenglW::~BKQOpenglW()
{
makeCurrent();
glDeleteVertexArrays(1,&VAO);
glDeleteBuffers(1,&VBO);
doneCurrent();
}
void BKQOpenglW::drawShapes(BKQOpenglW::Shape shape)
{
m_Shape = shape;
update();
}
void BKQOpenglW::setWireFrame(bool b)
{
makeCurrent();
if(b)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
update();
doneCurrent();
}
void BKQOpenglW::initializeGL()
{
initializeOpenGLFunctions();
// shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);
// shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSource);
shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shader/shader.vs");
shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shader/shader.fs");
shaderProgram.link();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//绑定ebo
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
//绑定纹理
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
pTexture = new QOpenGLTexture(QImage(":/images/images/brickwall.jpg").mirrored());
pTexture2 = new QOpenGLTexture(QImage(":/images/images/awesomeface.png").mirrored());
shaderProgram.bind();
shaderProgram.setUniformValue("texture1",0);
shaderProgram.setUniformValue("texture2",1);
//默认为GL_REPEAT 重复 GL_MIRRORED_REPEAT镜像 GL_CLAMP_TO_EDGE 边缘拉伸 GL_CLAMP_TO_BORDER 超出部分使用用户定义
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT); // we want to repeat the awesomeface pattern so we kept it at GL_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT);
QMatrix4x4 projection; //透视投影
//坐标到达观察空间之后,我们需要将其投影到裁剪坐标。
//裁剪坐标会被处理至-1.0到1.0的范围内,并判断哪些顶点将会出现在屏幕上
//参数1:指定视景体的视野的角度
//参数2:指定你的视景体的宽高比
//参数3:指定观察者到视景体的最近的裁剪面的距离(正数)
//参数4:指定观察者到视景体最远的裁剪面距离(正数)
projection.perspective(45.0f, 1.0f * width() / height(), 0.1f, 100.0f);
shaderProgram.setUniformValue("projection", projection);
shaderProgram.release();
timer.start();
time.start();
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
glBindBuffer(GL_ARRAY_BUFFER, 0);
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
glBindVertexArray(0);
}
void BKQOpenglW::resizeGL(int w, int h)
{
glViewport(0,0,w,h);
}
void BKQOpenglW::paintGL()
{
glEnable(GL_DEPTH_TEST);
glClearColor(0.2f, 0.3f, 0.3f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
QMatrix4x4 view; //观察矩阵,后退一点
float radius = 12.5;
float secs = time.elapsed()/1000.0;
float camX = sin(secs) * radius;
float camY = cos(secs) * radius;
view.lookAt(QVector3D(camX,0,camY), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
shaderProgram.setUniformValue("view", view);
shaderProgram.bind();
glBindVertexArray(VAO);
switch (m_Shape) {
case Triangle:
glDrawArrays(GL_TRIANGLES,0,3);
break;
case Rect:
pTexture->bind(0);
pTexture2->bind(1);
for (unsigned int i = 0; i < 10; i++) {
//计算模型矩阵
QMatrix4x4 model;
//平移
model.translate(cubePositions[i]);
//这样每个箱子旋转的速度就不一样
float angle = (i + 1.0f) * rotate;
//旋转
model.rotate(angle, QVector3D(1.0f, 0.3f, 0.5f));
//传入着色器并绘制
shaderProgram.setUniformValue("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
break;
default:
break;
}
}
最后的话:
?每天写代码时间不多,上班时间还是比较长,写博客不易,喜欢的朋友,可以关注偶。需要例子可以关注私信我,有时间就会回复。
|