总结一下上一节的内容:物体呈现的颜色与以下因素有关:

  • 物体的材质
  • 环境光
  • 物体对光源照射的光的漫反射
  • 物体对光源照射的光的镜面反射
  • 光源照射的光的颜色
  • 照射的角度

现实中的物体多半不是由单一的材料组成的,所以物体的材质是复杂的。

下面使用两张图片作为纹理,根据纹理坐标从图片获取像素代替材质的值来参与计算观察效果。

从左边的图获取像素参与计算环境光和漫反射。

从右边的图获取像素参与计算镜面反射。

从图片看中间的材料是木材,四周的是金属材料。右边的图中间是黑色就认为木材对镜面反射没有反应。

被照射物体的材质:

struct Material
{
    sampler2D diffuse;//漫反射纹理
    sampler2D specular;//镜面反射纹理
    float shininess;//反光度
};
uniform Material material;//被照射物体材质

移除了环境光材质颜色向量,因为环境光颜色在几乎所有情况下都等于漫反射颜色。

被照射物体的顶点着色器:

#version 450 core
layout (location = 0) in vec3 aPos;//顶点坐标
layout (location = 1) in vec3 aNormal;//法线数据
layout (location = 2) in vec2 aTexCoords;//纹理坐标

out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
    Normal = mat3(transpose(inverse(model))) * aNormal;//法线矩阵的公式 处理后得到法向量
    FragPos = vec3(model * vec4(aPos,1.0));
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoords = aTexCoords;
}

被照射物体的片段着色器:

#version 450 core
out vec4 FragColor;

in vec3 Normal;//法向量
in vec3 FragPos;//顶点

struct Material
{
    sampler2D diffuse;//漫反射纹理
    sampler2D specular;//镜面反射纹理
    float shininess;//反光度
};
uniform Material material;//被照射物体材质
in vec2 TexCoords;//纹理坐标

struct Light //光照强度分量
{
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;

uniform vec3 lightPos;//光源位置
uniform vec3 viewPos;//摄像机/眼睛位置

void main()
{
    //texture()从纹理中根据对应的纹理坐标取出对应的颜色值
    vec3 diffuseTexColor=vec3(texture(material.diffuse,TexCoords));
    vec3 specularTexColor=vec3(texture(material.specular,TexCoords));

    // 环境光
    vec3 ambient = diffuseTexColor * light.ambient;//环境光(物体反射的光源的颜色)

    // 漫反射
    vec3 norm = normalize(Normal);//法向量归一化
    vec3 lightDir = normalize(lightPos - FragPos);//顶点到光源的向量
    float diff = max(dot(norm, lightDir), 0.0);//法向量 与 顶点到光源的向量 进行点乘
    vec3 diffuse = diff * diffuseTexColor * light.diffuse;//漫反射光

    // 镜面反射
    float specularStrength = 0.5;//镜面反射系数
    vec3 viewDir = normalize(viewPos - FragPos);//摄像机位置 - 顶点位置
    vec3 reflectDir = reflect(-lightDir, norm);//光的折射向量
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);//摄像机 与 反射向量 的点乘
    vec3 specular = specularTexColor * spec * light.specular;

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}
#include "widget.h"
#include <QOpenGLShaderProgram>
#include <QTimer>
#include <QTime>

static uint VBO, VAO, lightVAO;

const float vertices[] = {
     //顶点坐标            法向量(每个面上的点法向量是相同的)      纹理坐标
    -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
};

static QVector3D lightColor(1.0f, 1.0f, 1.0f);
//static QVector3D lightColor(0.33f, 0.42f, 0.18f);
//static const QVector3D objectColor(1.0f, 0.5f, 0.31f);
static const QVector3D lightPos(1.2f, 1.0f, 2.0f);//光源位置
static const QVector3D viewPos(0.0,0.0,5.0);//摄像机/眼睛位置

Widget::Widget(QWidget *parent) : QOpenGLWidget(parent)
{
    auto timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,[this]{update();});
    timer->start(40);
}

Widget::~Widget()
{
    makeCurrent();
    glDeleteBuffers(1,&VBO);
    glDeleteVertexArrays(1,&VAO);
    glDeleteVertexArrays(1,&lightVAO);
    delete diffuseTex;
    delete specularTex;
    doneCurrent();
}

void Widget::initializeGL()
{
    initializeOpenGLFunctions();

    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    {
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), nullptr);
        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);
    }

    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);

    {
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), nullptr);
        glEnableVertexAttribArray(0);
    }

    {
        shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shapes.vert");
        shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shapes.frag");
        if(!shaderProgram.link())
            qDebug()<<"着色器程序1编译错误信息:"<<shaderProgram.log();
        shaderProgram.bind();

        shaderProgram.setUniformValue("material.shininess", 32.0f);

        shaderProgram.setUniformValue("lightPos",lightPos);
        shaderProgram.setUniformValue("viewPos",viewPos);

        diffuseTex = new QOpenGLTexture(QImage(":/container2.png").mirrored());
        specularTex = new QOpenGLTexture(QImage(":/container2_specular.png").mirrored());
        shaderProgram.bind();
        shaderProgram.setUniformValue("material.diffuse", 0);
        shaderProgram.setUniformValue("material.specular", 1);
    }

    {
        lightShaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/light.vert");
        lightShaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/light.frag");
        if(!lightShaderProgram.link())
            qDebug()<<"着色器程序2编译错误信息:"<<lightShaderProgram.log();
        lightShaderProgram.bind();
    }
}

void Widget::paintGL()
{
    static int rorateAngle = 0;
    ++rorateAngle;
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //投影矩阵
    QMatrix4x4 projection;
    projection.perspective(60,  //画面视野的角度 人眼在头部不动的情况下,视野大概为60°
                           (float)width()/height(), //窗口比例
                           0.1f,100);//参数三和四表示距离摄像机多近个多远范围内的物体要被显示。超出这个范围的物体将无法被显示。

    //视图矩阵
    QMatrix4x4 view;
    view.setToIdentity();//对矩阵进行单位化,保证所有矩阵运算都会在一个单位矩阵上进行
    view.lookAt(QVector3D(2, 1, 2),     //眼睛的位置
                QVector3D(0, 0, 0),     //眼睛看的位置
                QVector3D(0, 1, 0));    //此参数表示方向,相对于眼睛向上的方向。

    //模型矩阵
    QMatrix4x4 model;
    model.setToIdentity();
    model.rotate(rorateAngle, 1.0f, 5.0f, 0.5f);//旋转 该矩阵将坐标绕矢量 (x, y, z) 旋转angle度
    model.scale(0.6f);//缩放

    {
        shaderProgram.bind();
        shaderProgram.setUniformValue("projection", projection);
        shaderProgram.setUniformValue("model", model);
        shaderProgram.setUniformValue("view", view);

        shaderProgram.setUniformValue("light.ambient", 0.4f, 0.4f, 0.4f);
        shaderProgram.setUniformValue("light.diffuse", 0.9f, 0.9f, 0.9f);
        shaderProgram.setUniformValue("light.specular", 1.0f, 1.0f, 1.0f);
        shaderProgram.setUniformValue("material.shininess", 64.0f);

        diffuseTex->bind(0);
        specularTex->bind(1);

        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES,0,36);
    }

    {
        QVector3D lightPos(0.f, 1.0f, -2.0f);

        model.setToIdentity();
        model.translate(lightPos);
        model.rotate(1.0, 1.0f, 4.0f, 0.0f);
        model.scale(0.2f);

        lightShaderProgram.bind();
        lightShaderProgram.setUniformValue("projection", projection);
        lightShaderProgram.setUniformValue("view", view);
        lightShaderProgram.setUniformValue("model", model);
        lightShaderProgram.setUniformValue("lightColor",lightColor);

        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES,0,36);
    }
}

 

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐