您当前的位置: 首页 >  qt

龚建波

暂无认证

  • 3浏览

    0关注

    313博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

OpenGL with QtWidgets:材质、光照贴图

龚建波 发布时间:2021-09-21 00:13:41 ,浏览量:3

(本文是LearnOpenGL的学习笔记, 教程中文翻译地址https://learnopengl-cn.github.io/(备用地址https://learnopengl-cn.readthedocs.io/zh/latest/),写于 2021-9-20)

0.前言

上一节学习了基础光照(https://mp.csdn.net/mp_blog/creation/editor/106005766),教程接下来讲了材质和光照贴图。

1.知识点

如果我们想要在OpenGL中模拟多种类型的物体,我们必须为每个物体分别定义一个材质(Material)属性。可以用这三个分量来定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对物体的颜色输出有着精细的控制了。再添加反光度(Shininess)这个分量到上述的三个颜色中,这就有我们需要的所有材质属性了:

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 
uniform Material material;

ambient材质向量定义了在环境光照下这个物体反射得是什么颜色,通常这是和物体颜色相同的颜色。diffuse材质向量定义了在漫反射光照下物体的颜色。(和环境光照一样)漫反射颜色也要设置为我们需要的物体颜色。specular材质向量设置的是镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。最后,shininess影响镜面高光的散射/半径。 

现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。引入漫反射和镜面光贴图(Map)。这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。

struct Material {
    sampler2D diffuse; //环境光和漫反射取同一个纹理
    sampler2D specular;
    float shininess;
};
2.实现代码

(项目git链接:https://github.com/gongjianbo/OpenGLwithQtWidgets.git)

为了简化代码,我把 Camrea 观察矩阵给固定了不能旋转和移动

材质效果:

光照贴图效果:

光照贴图部分的代码(GLLightingMap类)如下:

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//光照贴图
//QOpenGLWidget窗口上下文
//QOpenGLFunctions访问OpenGL接口,可以不继承作为成员变量使用
class GLLightingMap
        : public QOpenGLWidget
        , protected QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    explicit GLLightingMap(QWidget *parent = nullptr);
    ~GLLightingMap();

protected:
    //【】继承QOpenGLWidget后重写这三个虚函数
    //设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
    void initializeGL() override;
    //渲染OpenGL场景,每当需要更新小部件时使用
    void paintGL() override;
    //设置OpenGL视口、投影等,每当尺寸大小改变时调用
    void resizeGL(int width, int height) override;

private:
    void initShader();
    QOpenGLTexture *initTexture(const QString &imgpath);

private:
    //着色器程序
    QOpenGLShaderProgram lightingShader,lampShader;
    //顶点数组对象
    QOpenGLVertexArrayObject lightingVao,lampVao;
    //顶点缓冲
    QOpenGLBuffer vbo;
    //纹理
    QOpenGLTexture *diffuseMap{ nullptr };
    QOpenGLTexture *specularMap{ nullptr };
    //
    QTimer timer;
    int rotate{ 0 };
};

#include "GLLightingMap.h"
#include 
#include 
#include 

GLLightingMap::GLLightingMap(QWidget *parent)
    : QOpenGLWidget(parent)
{
    connect(&timer,&QTimer::timeout,this,[this](){
        rotate+=1;
        if(isVisible()){
            update();
        }
    });
    timer.setInterval(50);
}

GLLightingMap::~GLLightingMap()
{
    //initializeGL在显示时才调用,释放未初始化的会异常
    if(!isValid())
        return;
    //QOpenGLWidget
    //三个虚函数不需要makeCurrent,对应的操作已由框架完成
    //但是释放时需要设置当前上下文
    makeCurrent();
    vbo.destroy();
    lightingVao.destroy();
    lampVao.destroy();
    delete diffuseMap;
    delete specularMap;
    doneCurrent();
}

void GLLightingMap::initializeGL()
{
    //为当前上下文初始化OpenGL函数解析
    initializeOpenGLFunctions();
    initShader();

    //方块的顶点、法向量、纹理坐标
    float vertices[] = {
        // positions          // normals           // 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=QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    vbo.create();

    //light vao
    lightingVao.create();
    lightingVao.bind();
    vbo.bind();
    vbo.allocate(vertices,sizeof(vertices));
    //setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
    lightingShader.setAttributeBuffer(0, GL_FLOAT, sizeof(GLfloat) * 0, 3, sizeof(GLfloat) * 8);
    lightingShader.enableAttributeArray(0);
    lightingShader.setAttributeBuffer(1, GL_FLOAT, sizeof(GLfloat) * 3, 3, sizeof(GLfloat) * 8);
    lightingShader.enableAttributeArray(1);
    lightingShader.setAttributeBuffer(2, GL_FLOAT, sizeof(GLfloat) * 6, 2, sizeof(GLfloat) * 8);
    lightingShader.enableAttributeArray(2);
    vbo.release();
    lightingVao.release();

    //lamp vao
    lampVao.create();
    lampVao.bind();
    vbo.bind();
    //setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
    lampShader.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(GLfloat) * 8);
    lampShader.enableAttributeArray(0);
    vbo.release();
    lampVao.release();

    //纹理
    diffuseMap = initTexture(":/container2.png");
    specularMap = initTexture(":/container2_specular.png");
    //shader configuration
    lightingShader.bind();
    lightingShader.setUniformValue("material.diffuse", 0);
    lightingShader.setUniformValue("material.specular", 1);
    lightingShader.release();

    timer.start();
}

void GLLightingMap::paintGL()
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    //清除深度缓冲
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //Z缓冲(Z-buffer),深度缓冲(Depth Buffer)。
    glEnable(GL_DEPTH_TEST);

    //draw lighting
    lightingShader.bind();
    QMatrix4x4 view; //观察矩阵
    view.translate(0.0f, 0.0f, -5.0f);
    view.rotate(45, QVector3D(1.0f, 0.8f, 0.0f));
    lightingShader.setUniformValue("view", view);
    QMatrix4x4 projection; //透视投影
    projection.perspective(45.0f, 1.0f * width() / height(), 0.1f, 100.0f);
    lightingShader.setUniformValue("projection", projection);
    QMatrix4x4 model;//模型矩阵
    lightingShader.setUniformValue("model", model);
    //因为要获取灯的位置,所以提前算灯的model矩阵
    model = QMatrix4x4();
    model.translate(QVector3D(1.0f, 1.0f, -1.0f));
    model.scale(0.3f);
    QVector3D light_pos = model.map(QVector3D(0.0f, 0.0f, 0.0f));
    QMatrix4x4 vv = view.inverted(); //逆矩阵求观察点位置
    QVector3D view_pos = vv.map(QVector3D(0.0f, 0.0f, 0.0f));
    lightingShader.setUniformValue("light.position", light_pos);
    lightingShader.setUniformValue("viewPos", view_pos);
    //光照-light properties
    QVector3D light_color = QVector3D(1.0f, 1.0f, 1.0f);
    QVector3D diffuse_color = light_color * 0.5f; // decrease the influence
    QVector3D ambient_color = diffuse_color * 0.2f; // low influence
    lightingShader.setUniformValue("light.ambient", ambient_color);
    lightingShader.setUniformValue("light.diffuse", diffuse_color);
    lightingShader.setUniformValue("light.specular", QVector3D(1.0f, 1.0f, 1.0f));

    //材质-material properties
    //shininess影响镜面高光的散射/半径
    lightingShader.setUniformValue("material.shininess", 64.0f);
    lightingVao.bind();
    //绑定2d纹理
    //bind diffuse map
    glActiveTexture(GL_TEXTURE0);
    diffuseMap->bind();
    //bind specular map
    glActiveTexture(GL_TEXTURE1);
    specularMap->bind();
    glDrawArrays(GL_TRIANGLES, 0, 36);
    lightingVao.release();
    lightingShader.release();

    //draw lamp
    lampShader.bind();
    lampShader.setUniformValue("view", view);
    lampShader.setUniformValue("projection", projection);
    lampShader.setUniformValue("model", model);
    lampVao.bind();
    glDrawArrays(GL_TRIANGLES, 0, 36);
    lampVao.release();
    lampShader.release();
}

void GLLightingMap::resizeGL(int width, int height)
{
    glViewport(0, 0, width, height);
}

void GLLightingMap::initShader()
{
    //lingting shader
    //in输入,out输出,uniform从cpu向gpu发送
    const char *lighting_vertex=R"(#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;
    TexCoords = aTexCoords;

    gl_Position = projection * view * vec4(FragPos, 1.0);
})";
    const char *lighting_fragment=R"(#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    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()
{
    // ambient
    vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;

    // diffuse
    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;

    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
})";

    //将source编译为指定类型的着色器,并添加到此着色器程序
    if(!lightingShader.addCacheableShaderFromSourceCode(
                QOpenGLShader::Vertex,lighting_vertex)){
        qDebug()            
关注
打赏
1655829268
查看更多评论
0.2530s