Qt3D改变观察视角例程(一)
在3D显示中,有Model矩阵、View矩阵和Project矩阵。简称为MVP矩阵。这里实现的是改变View矩阵中的观察点的位置,视野中心不变。亦即站在一个圆环的不同地方朝圆心观察。本文显示的是一个平面(地面)上面悬浮一个四面体,鼠标按下移动来改变观察点和方向。关于摄像机姿势的概念可以参考以下网页内容,主要是欧拉角的变换:
本文代码效果图是:
下面是头文件,测试环境是VS2017和Qt5.9:
class QOpenGLTexture; class QOpenGLBuffer; class MuOpenGLWidget : public QOpenGLWidget, private QOpenGLFunctions { Q_OBJECT private: struct VertexData { QVector3D pos; QVector3D normal; QVector3D color; }; public: MuOpenGLWidget(QWidget* parent = 0); private: void initializeGL() override; void paintGL() override; void resizeGL(int w, int h) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; QVector3D calcDirVec() const; private: QOpenGLShaderProgram* program; QOpenGLBuffer* vertexBuff; QOpenGLBuffer* indexBuff; /* 下面2个是相机方向,都为0时相机朝向X轴正方向 */ float yaw; /* 绕Y轴旋转 */ float pitch; /* 绕X轴旋转 */ /* 下面1个是鼠标上次点击的位置 */ QPoint lastPos; };
CPP文件:
MuOpenGLWidget::MuOpenGLWidget(QWidget* parent) : QOpenGLWidget(parent), lastPos(0xDEADBEEFi32, 0xDEADBEEFi32) { yaw = -90.0f; pitch = 0; QSurfaceFormat surface; surface.setSamples(4); setFormat(surface); } void MuOpenGLWidget::initializeGL() { initializeOpenGLFunctions(); glEnable(GL_DEPTH_TEST); program = new QOpenGLShaderProgram(this); const char* vsrc = u8R"( #version 330 core layout (location = 0) in vec3 aPos; // 空间坐标 layout (location = 1) in vec3 aNormal; // 法线向量 layout (location = 2) in vec3 aColor; // 顶点颜色 uniform mat4 modelMatrix; uniform mat4 mvpMatrix; out vec3 oNormal; out vec3 oFragPos; out vec3 oColor; void main() { oFragPos = vec3(modelMatrix * vec4(aPos, 1.0)); oNormal = mat3(transpose(inverse(modelMatrix))) * aNormal; oColor = aColor; gl_Position = mvpMatrix * vec4(aPos, 1.0); })"; program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc); const char* fsrc = u8R"( #version 330 core uniform vec3 lightPos; uniform vec3 viewPos; uniform vec3 lightColor; in vec3 oNormal; in vec3 oFragPos; in vec3 oColor; void main() { // 环境光照 float ambientStrength = 0.4; vec3 ambient = ambientStrength * lightColor; // 漫反射光照 vec3 norm = normalize(oNormal); vec3 lightDir = normalize(lightPos - oFragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = diff * lightColor; // 镜面光照 float specularStrength = 1.0; vec3 viewDir = normalize(viewPos - oFragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); vec3 specular = specularStrength * spec * lightColor; // 把纹理中的颜色和光照参数相乘 gl_FragColor = vec4((ambient + diffuse + specular) * oColor, 1.0); })"; program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc); program->link(); program->bind(); VertexData vertices[] = { { QVector3D(-0.433f, 0, -0.25f), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(1.0f, 0, 0) }, { QVector3D(0, 0, 0.5f), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(0, 1.0f, 0) }, { QVector3D(0, 0.707f, 0), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(0, 0, 1.0f) }, { QVector3D(0, 0, 0.5f), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(1.0f, 1.0f, 0) }, { QVector3D(0.433f, 0, -0.25f), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(1.0f, 0, 1.0f) }, { QVector3D(0, 0.707f, 0), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(0, 1.0f, 1.0f) }, { QVector3D(0.433f, 0, -0.25f), QVector3D(0, 0.333f, -0.943f), QVector3D(0, 0, 1.0f) }, { QVector3D(-0.433f, 0, -0.25f), QVector3D(0, 0.333f, -0.943f), QVector3D(0, 1.0f, 0) }, { QVector3D(0, 0.707f, 0), QVector3D(0, 0.333f, -0.943f), QVector3D(1.0f, 0, 0) }, { QVector3D(0, 0, 0.5f), QVector3D(0, -1.0f, 0), QVector3D(0, 1.0f, 1.0f) }, { QVector3D(-0.433f, 0, -0.25f), QVector3D(0, -1.0f, 0), QVector3D(1.0f, 1.0f, 0) }, { QVector3D(0.433f, 0, -0.25f), QVector3D(0, -1.0f, 0), QVector3D(1.0f, 0, 1.0f) }, /* 后面6个点绘制水平的地面 */ { QVector3D(-10.0f, -0.2f, -10.0f), QVector3D(0, 1.0f, 0), QVector3D(0, 1.0f, 1.0f) }, { QVector3D(-10.0f, -0.2f, 10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 1.0f, 0) }, { QVector3D(10.0f, -0.2f, -10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 0, 1.0f) }, { QVector3D(10.0f, -0.2f, -10.0f), QVector3D(0, 1.0f, 0), QVector3D(0, 1.0f, 1.0f) }, { QVector3D(-10.0f, -0.2f, 10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 1.0f, 0) }, { QVector3D(10.0f, -0.2f, 10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 0, 1.0f) }, }; GLushort indices[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, }; vertexBuff = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); vertexBuff->create(); vertexBuff->bind(); vertexBuff->allocate(vertices, 18 * sizeof(VertexData)); indexBuff = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); indexBuff->create(); indexBuff->bind(); indexBuff->allocate(indices, 18 * sizeof(float)); int offset = 0; int vertexLocation = program->attributeLocation("aPos"); program->enableAttributeArray(vertexLocation); program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData)); offset += sizeof(QVector3D); int normalLocation = program->attributeLocation("aNormal"); program->enableAttributeArray(normalLocation); program->setAttributeBuffer(normalLocation, GL_FLOAT, offset, 3, sizeof(VertexData)); offset += sizeof(QVector3D); int colorLocation = program->attributeLocation("aColor"); program->enableAttributeArray(colorLocation); program->setAttributeBuffer(colorLocation, GL_FLOAT, offset, 3, sizeof(VertexData)); } #define LIGHT_COLOR QVector3D(0.9f, 1.0f, 2.0f) /* 这里的颜色值可以大于1不知道为什么 */ #define LIGHT_POS QVector3D(2.5f, 5.8f, 2.0f) #define VIEW_CENTER QVector3D(0.0f, 0.25f, 0.0f) void MuOpenGLWidget::paintGL() { QMatrix4x4 projection; projection.perspective(45.0f, 1, 0.1f, 100.0f); QMatrix4x4 viewMatrix; QVector3D eyeDir = calcDirVec(); QVector3D eyePos = VIEW_CENTER - 10 * eyeDir; viewMatrix.lookAt(eyePos, VIEW_CENTER, QVector3D(0, 1, 0)); QMatrix4x4 modelMatrix; modelMatrix.translate(0, -0.8, 0); // 平移 modelMatrix.rotate(0, 0, 1); // 旋转 modelMatrix.scale(4.0); // 缩放 QMatrix4x4 mvpMatrix = projection * viewMatrix * modelMatrix; program->setUniformValue("lightColor", LIGHT_COLOR); program->setUniformValue("lightPos", LIGHT_POS); program->setUniformValue("viewPos", eyePos); program->setUniformValue("mvpMatrix", mvpMatrix); program->setUniformValue("modelMatrix", modelMatrix); glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_SHORT, 0); } #undef LIGHT_COLOR #undef LIGHT_POS #undef EYE_CENTER void MuOpenGLWidget::resizeGL(int w, int h) { } void MuOpenGLWidget::mousePressEvent(QMouseEvent *event) { lastPos = event->pos(); } void MuOpenGLWidget::mouseReleaseEvent(QMouseEvent *event) { lastPos = QPoint(0xDEADBEEFi32, 0xDEADBEEFi32); } void MuOpenGLWidget::mouseMoveEvent(QMouseEvent *event) { float xoffset = event->pos().x() - lastPos.x(); float yoffset = lastPos.y() - event->pos().y(); lastPos = event->pos(); float sensitivity = 0.25f; xoffset *= sensitivity; yoffset *= sensitivity; yaw += xoffset; pitch += yoffset; pitch = std::max(-89.0f, std::min(89.0f, pitch)); update(); } QVector3D MuOpenGLWidget::calcDirVec() const { QVector3D front; front.setX(cosf(qDegreesToRadians(yaw)) * cosf(qDegreesToRadians(pitch))); front.setY(sinf(qDegreesToRadians(pitch))); front.setZ(sinf(qDegreesToRadians(yaw)) * cosf(qDegreesToRadians(pitch))); return front.normalized(); }