10.光照+纹理
物体的顶点着色器lighting_maps
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; out vec3 FragPos; out vec3 Normal; out vec2 TexCoords; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { FragPos = vec3(model * vec4(aPos, 1.0));//将顶点位置属性乘以模型矩阵变换到世界空间坐标就得到了片段位置 Normal = mat3(transpose(inverse(model))) * aNormal;//生成法线矩阵(3*3)使它失去位移属性以及能够乘以vec3的向量 TexCoords = aTexCoords; gl_Position = projection * view * vec4(FragPos, 1.0); }
物体的片段着色器
#version 330 core out vec4 FragColor; struct Material { //环境光颜色几乎在所有情况下都等于漫反射颜色所以只用diffuse sampler2D diffuse;//不透明类型,不能实例化只能通过uniform来定义它 //纹理1 木箱 用它来进行漫反射 sampler2D specular; //纹理2 铁边,用它来反射高光 float shininess; }; struct Light { vec3 position;//是一个静态的变量 vec3 ambient; vec3 diffuse; vec3 specular; }; in vec3 FragPos;//片段位置 in vec3 Normal;//法向量 in vec2 TexCoords; uniform vec3 viewPos;//摄像机的位置坐标 uniform Material material; uniform Light light; void main() { // 环境光照 vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;//从纹理中采样材料的环境光照,参数与漫反射一致 // 漫反射光照 vec3 norm = normalize(Normal);//先将法向量标准化 vec3 lightDir = normalize(light.position - FragPos);//从片段位置指向光源位置的向量 float diff = max(dot(norm, lightDir), 0.0);//计算官员对当前片段实际的漫反射影响,光照路径与法向量的角度越大,那么漫反射的分量就会越小 vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb; //从纹理中采样材料的漫反射 // 镜面反射 vec3 viewDir = normalize(viewPos - FragPos);//从片段位置指向摄像机位置的向量 vec3 reflectDir = reflect(-lightDir, norm); //reflect函数的第一个参数是从光源指向片段位置的向量,第二个参数是法向量 //计算镜面分量 float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);//先计算视线方向和反射方向的点乘(确保不是负值),后面的参数越大代表高光点越小,散射的越少(2 4 8--256) vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb; //从纹理中采样材料的镜面反射 vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }
灯的顶点着色器
#version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main(){ gl_Position = projection * view * model * vec4(aPos, 1.0f); }
灯的片段着色器
#version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0f); }
照相机类省略
头文件
#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: bool createShader(); QOpenGLTexture* loadTexture(const QString& path); private: QOpenGLShaderProgram lightingShader, lampShader; QOpenGLBuffer vbo; QOpenGLVertexArrayObject cubeVAO, lightVAO; QOpenGLTexture* m_pDiffuseMap = nullptr; QOpenGLTexture* m_pSpecularMap = nullptr; QTimer* m_pTimer = nullptr; int m_nTimeValue = 0; // camera std::unique_ptr<Camera> camera; bool m_bLeftPressed; QPoint m_lastPos; };
源文件
#include "QtFunctionWidget.h" #include <QDebug> #include <QTimer> // lighting static QVector3D lightPos(1.2f, 1.0f, 2.0f); QtFunctionWidget::QtFunctionWidget(QWidget *parent) : QOpenGLWidget(parent), vbo(QOpenGLBuffer::VertexBuffer) { camera = std::make_unique<Camera>(QVector3D(0.0f, 0.0f, 3.0f)); m_bLeftPressed = false; //为了保证帧率,这里40毫秒刷新一次,update函数也可以放在鼠标键盘事件这些函数中 m_pTimer = new QTimer(this); connect(m_pTimer, &QTimer::timeout, this, [=] { m_nTimeValue += 1; update(); }); m_pTimer->start(40);//25 fps } QtFunctionWidget::~QtFunctionWidget() { //保证线程安全 makeCurrent(); lightVAO.destroy(); cubeVAO.destroy(); vbo.destroy(); //纹理 if (m_pDiffuseMap) { delete m_pDiffuseMap; } if (m_pSpecularMap) { delete m_pSpecularMap; } doneCurrent(); } void QtFunctionWidget::initializeGL() { this->initializeOpenGLFunctions(); //创建着色器 createShader(); float vertices[] = { // positions // 每个面的法向量 // texture coords -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }; vbo.create(); vbo.bind(); vbo.allocate(vertices, sizeof(vertices)); //物体的顶点数组 { QOpenGLVertexArrayObject::Binder vaoBind(&cubeVAO); //顶点数组 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); //法向量数组 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); //纹理数组 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); } { //灯的顶点数组 QOpenGLVertexArrayObject::Binder vaoBind(&lightVAO); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); } //纹理 m_pDiffuseMap = loadTexture("container2.png");//木箱 m_pSpecularMap = loadTexture("container2_specular.png");//铁质边框 lightingShader.bind(); lightingShader.setUniformValue("material.diffuse", 0);//把需要用的纹理单元赋值到material.diffuse,并绑定箱子的纹理到这个纹理单元 lightingShader.setUniformValue("material.specular", 1); lightingShader.release(); vbo.release(); //深度测试 glEnable(GL_DEPTH_TEST); } void QtFunctionWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); } void QtFunctionWidget::paintGL() { camera->processInput(0.5f); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); lightingShader.bind(); lightingShader.setUniformValue("light.position", lightPos); lightingShader.setUniformValue("viewPos", camera->position); // 灯的属性 lightingShader.setUniformValue("light.ambient", QVector3D(0.2f, 0.2f, 0.2f)); lightingShader.setUniformValue("light.diffuse", QVector3D(0.5f, 0.5f, 0.5f)); lightingShader.setUniformValue("light.specular", QVector3D(1.0f, 1.0f, 1.0f)); // 材料的属性 lightingShader.setUniformValue("material.shininess", 256.0f);//参数是2的倍数,最多是256 //投影矩阵 视图矩阵 QMatrix4x4 projection; projection.perspective(camera->zoom, 1.0f * width() / height(), 0.1f, 100.0f); QMatrix4x4 view = camera->getViewMatrix(); lightingShader.setUniformValue("projection", projection); lightingShader.setUniformValue("view", view); // 物体的模型矩阵 QMatrix4x4 model; lightingShader.setUniformValue("model", model); // 添加第一个纹理 glActiveTexture(GL_TEXTURE0); m_pDiffuseMap->bind(); // 添加第二个纹理 glActiveTexture(GL_TEXTURE1); m_pSpecularMap->bind(); {// 绑定立方体的vao QOpenGLVertexArrayObject::Binder vaoBind(&cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); } lightingShader.release(); // 灯 lampShader.bind(); lampShader.setUniformValue("projection", projection); lampShader.setUniformValue("view", view); model = QMatrix4x4(); model.translate(lightPos); model.scale(0.2f); lampShader.setUniformValue("model", model); { QOpenGLVertexArrayObject::Binder vaoBind(&lightVAO); glDrawArrays(GL_TRIANGLES, 0, 36); } lampShader.release(); } 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) { if (event->button() == Qt::LeftButton) { m_bLeftPressed = true; m_lastPos = event->pos(); } } void QtFunctionWidget::mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); m_bLeftPressed = false; } void QtFunctionWidget::mouseMoveEvent(QMouseEvent *event) { if (m_bLeftPressed) { int xpos = event->pos().x(); int ypos = event->pos().y(); int xoffset = xpos - m_lastPos.x(); int yoffset = m_lastPos.y() - ypos; m_lastPos = event->pos(); camera->processMouseMovement(xoffset, yoffset); } } void QtFunctionWidget::wheelEvent(QWheelEvent *event) { QPoint offset = event->angleDelta(); camera->processMouseScroll(offset.y() / 20.0f); } //创建着色器 bool QtFunctionWidget::createShader() { bool success = lightingShader.addShaderFromSourceFile(QOpenGLShader::Vertex, "lighting_maps.vert"); if (!success) { qDebug() << "shaderProgram addShaderFromSourceFile failed!" << lightingShader.log(); return success; } success = lightingShader.addShaderFromSourceFile(QOpenGLShader::Fragment, "lighting_maps.frag"); if (!success) { qDebug() << "shaderProgram addShaderFromSourceFile failed!" << lightingShader.log(); return success; } success = lightingShader.link(); if (!success) { qDebug() << "shaderProgram link failed!" << lightingShader.log(); } success = lampShader.addShaderFromSourceFile(QOpenGLShader::Vertex, "lamp.vert"); if (!success) { qDebug() << "shaderProgram addShaderFromSourceFile failed!" << lampShader.log(); return success; } success = lampShader.addShaderFromSourceFile(QOpenGLShader::Fragment, "lamp.frag"); if (!success) { qDebug() << "shaderProgram addShaderFromSourceFile failed!" << lampShader.log(); return success; } success = lampShader.link(); if (!success) { qDebug() << "shaderProgram link failed!" << lampShader.log(); } return success; } //加载纹理的函数 QOpenGLTexture *QtFunctionWidget::loadTexture(const QString &path) { QOpenGLTexture* pTexture = new QOpenGLTexture(QImage(path), QOpenGLTexture::GenerateMipMaps); if (!pTexture->isCreated()) { qDebug() << "Failed to load texture"; } pTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); pTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); pTexture->setMinificationFilter(QOpenGLTexture::Linear); pTexture->setMagnificationFilter(QOpenGLTexture::Linear); return pTexture; }
效果