QT+OpenGL光照2

简介: 在现实世界中,每个物体会对光照产生不同的反应

QT+OpenGL材质

本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主


材质

在现实世界中,每个物体会对光照产生不同的反应

724bdea372ab42539a281ceec5cf1b29.png

在OpenGL中模拟多种类型的物体,必须为每种物体分别定义一个材质属性

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

这个时候如果我们再去掉shader内写死的环境光的分量的话

#version 330 core
struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
uniform Material material;
out vec4 FragColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
in vec3 Normal;
in vec3 fragPos;
void main()
{
    // ambient
    vec3 ambient = lightColor;
    // diffuse
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - fragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;
    // specular
    vec3 viewDir = normalize(viewPos - fragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = spec * lightColor;
    vec3 result = (ambient * material.ambient +
                   diffuse * material.diffuse +
                   specular * material.specular);
    FragColor = vec4(result, 1.0);
}

整体显示会非常亮

e20004df3fe14b3ebf56463434d53789.png

添加材质,并且改变颜色

shader_program_.setUniformValue("material.ambient",  1.0f, 0.5f, 0.31f);
shader_program_.setUniformValue("material.diffuse",  1.0f, 0.5f, 0.31f);
shader_program_.setUniformValue("material.specular", 0.5f, 0.5f, 0.5f);
shader_program_.setUniformValue("material.shininess", 32.0f);
lightColor.setX(sin(time/100 *2.0f));
lightColor.setY(sin(time/100 *0.7f));
lightColor.setZ(sin(time/100 *1.3f));
QVector3D diffuseColor = lightColor * QVector3D(0.5, 0.5, 0.5);
QVector3D ambientColor = lightColor * QVector3D(0.2, 0.2, 0.2);
shader_program_.setUniformValue("light.ambient",  ambientColor);
shader_program_.setUniformValue("light.diffuse",  diffuseColor); // 将光照调暗了一些以搭配场景
shader_program_.setUniformValue("light.specular", lightColor);

光照贴图

现实世界中的物体通常不只有一种材质,而是由多种材质组成


 ●  所以我们需要拓展之前的系统,引入漫反射和镜面光贴图


漫反射贴图

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


注意sampler2D是所谓的不透明类型(Opaque Type),也就是说我们不能将它实例化,只能通过uniform来定义它。如果我们使用除uniform以外的方法(比如函数的参数)实例化这个结构体,GLSL会抛出一些奇怪的错误。这同样也适用于任何封装了不透明类型的结构体。

struct Material {
    sampler2D diffuse;
    vec3      specular;
    float     shininess;
}; 

镜面光贴图

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
}; 

平行光

3a26a95150bc4475b0403ab5bb267579.png

现实世界中,我们有很多种类的光照,每种的表现都不同。当一个光源处于很远的地方时,来自光源的每条光线就会近似于相互平行

struct Light {
    // vec3 position; // 使用定向光就不再需要了
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
  vec3 lightDir = normalize(-light.direction);
  ...
}

点光源

769e49c497df43e2b4745e3512d6d21a.png

点光源是处于世界中的某一个位置的光源,他会朝着所有方向发光,但是光线会随着距离逐渐衰减。想象作为投光物的火把或者灯泡,他们都是点光源。


衰减:

点光源会随着光线传播距离的增长逐渐削减光的强度。


衰减公式如下:

屏幕截图 2023-08-03 153600.png


在这里d代表了片段距光源的距离。接下来为了计算衰减值,我们定义3个(可配置的)项:常数项Kc、一次项Kl和二次项Kq。

#version 330 core
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};
uniform Material material;
struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant;
    float linear;
    float quadratic;
};
uniform Light light;
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
in vec3 Normal;
in vec3 fragPos;
in vec2 TexCoords;
void main()
{
    vec3 diffuseColor = vec3(texture(material.diffuse, TexCoords));
    vec3 specularColor = vec3(texture(material.specular, TexCoords));
    float distance    = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance +
    light.quadratic * (distance * distance));
    // ambient
    vec3 ambient =  diffuseColor * light.ambient;
    // diffuse
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - fragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse =  diff * diffuseColor * light.diffuse;
    // specular
    vec3 viewDir = normalize(viewPos - fragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular =  spec * light.specular * specularColor;
    // mix
    ambient  *= attenuation;
    diffuse  *= attenuation;
    specular *= attenuation;
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
}

聚光


e713baa2df7a4b6fbb3476d139485ce4.png

LightDir : 从片段指向光源的向量


SpotDir: 聚光所指方向


Phi ϕ \phiϕ: 指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮


Theta θ \thetaθ:LightDir向量和SpotDir向量之间的夹角。在聚光内部的话θ \thetaθ值应该比ϕ \phiϕ值小。


手电筒就是普通的聚光灯,但是他的位置和方向会随着人的运动而改变。

#version 330 core
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};
uniform Material material;
struct Light {
    vec3 position;
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant;
    float linear;
    float quadratic;
    float cutOff;
};
uniform Light light;
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
in vec3 Normal;
in vec3 fragPos;
in vec2 TexCoords;
void main()
{
    vec3 diffuseColor = vec3(texture(material.diffuse, TexCoords));
    vec3 specularColor = vec3(texture(material.specular, TexCoords));
    vec3 lightDir = normalize(fragPos - light.position);
    float theta = dot(lightDir, normalize(light.direction));
    if(theta > light.cutOff)
    {
        // ambient
        vec3 ambient =  diffuseColor * light.ambient;
        // diffuse
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(light.position - fragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse =  diff * diffuseColor * light.diffuse;
        // specular
        vec3 viewDir = normalize(viewPos - fragPos);
        vec3 reflectDir = reflect(-lightDir, norm);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
        vec3 specular =  spec * light.specular * specularColor;
        // attenuation
        float distance    = length(light.position - fragPos);
        float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
        // mix
        ambient  *= attenuation;
        diffuse  *= attenuation;
        specular *= attenuation;
        vec3 result = ambient + diffuse + specular;
        FragColor = vec4(result, 1.0);
    }
    else
    {
        FragColor = vec4(light.ambient * diffuseColor, 1.0);
    }
}

d72fc7631045474190e12915a238faa3.png

聚光边缘太过生硬,因此需要对边缘进行平滑软化;公式如下

屏幕截图 2023-08-03 153833.png

cd1c02d4bab04874bcb6c072bcdc6899.png

多光源

创建一个包含六个光源的场景,我们将模拟一个类似太阳的定向光源,四个分散在场景中的点光源和一个手电筒

#version 330 core
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};
uniform Material material;
struct SpotLight {
    vec3 position;
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant;
    float linear;
    float quadratic;
    float cutOff;
    float outerCutOff;
};
struct DirLight {
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirLight dirLight;
uniform SpotLight spotLight;
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
in vec3 Normal;
in vec3 fragPos;
in vec2 TexCoords;
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir);
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 diffuseColor = vec3(texture(material.diffuse, TexCoords));
vec3 specularColor = vec3(texture(material.specular, TexCoords));
void main()
{
    vec3 norm = normalize(Normal);
    vec3 viewDir = normalize(viewPos - fragPos);
    vec3 result = vec3(0);
    result += CalcSpotLight(spotLight, norm, viewDir);
    result += CalcDirLight(dirLight, norm, viewDir);
    FragColor = vec4(result, 1.0);
}
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - fragPos);
    // ambient
    vec3 ambient =  diffuseColor * light.ambient;
    // diffuse
    float diff = max(dot(normal, lightDir), 0.0);
    vec3 diffuse =  diff * diffuseColor * light.diffuse;
    // specular
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular =  spec * light.specular * specularColor;
    // attenuation
    float distance    = length(light.position - fragPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    //ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    //smooth
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
    diffuse *= intensity;
    specular *= intensity;
    return (ambient + diffuse + specular);
}
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    // ambient
    float diff = max(dot(normal, lightDir), 0.0);
    // specular
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // attenuation
    vec3 ambient  = light.ambient  * diffuseColor;
    vec3 diffuse  = light.diffuse  * diff * diffuseColor;
    vec3 specular = light.specular * spec * specularColor;
    return (ambient + diffuse + specular);
}

光照部分到此结束, 实现部分请参照gitee代码。如果您不能运行,可以联系私信博主咨询。

目录
相关文章
|
4月前
QT4.7版本的OPENGL的3D旋转模型例子
QT4.7版本的OPENGL的3D旋转模型例子
|
2月前
|
Linux API iOS开发
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
37 0
|
2月前
|
机器学习/深度学习 API vr&ar
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
117 4
|
2月前
|
Linux Windows
imx6ull开发板之qt应用编程读取AP3216c(光照,距离)数据。
imx6ull开发板之qt应用编程读取AP3216c(光照,距离)数据。
21 0
|
2月前
Qt+OpenGL 打砖块游戏
Qt+OpenGL 打砖块游戏
21 0
|
3月前
|
数据可视化 API vr&ar
qt“五彩斑斓“ opengl
qt“五彩斑斓“ opengl
|
7月前
QT圆形进度条(QT桌面项目光照强度检测)
QT圆形进度条(QT桌面项目光照强度检测)
40 0
|
9月前
|
算法
QT+OpenGL高级光照 Blinn-Phong和Gamma校正
冯氏光照:视线与反射方向之间的夹角不小于90度,镜面光分量会变成0.0(不是很合理,会有清晰的分界线) Blinn-Phone模型采用了半程向量,即光线与视线夹角一般方向上的一个单位向量。当半程向量与法线向量越接近,镜面光分量就越大。
62 0
|
9月前
|
编解码 异构计算
QT+OpenGL实例化和抗锯齿
如果将数据一次性发送给GPU,然后使用一个绘制函数让OpenGL利用这些数据绘制多个物体,就会方便了。这就是实例化(Instancing)。
166 0
|
9月前
|
数据可视化
QT+OpenGL几何着色器
输入布局限定符可以从顶点着色器接收下列任何一个图元值: ● points:绘制GL_POINTS图元时 ● lines:绘制GL_LINES或GL_LINE_STRIP时 ● lines_adjacency:GL_ADJACENCY或GL_LINESTRIP_ADJACENCY ● triangles:GL_TRIANGLES、GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN ● triangles_adjacency:GL_TRIANGLES_ADJACENCY或GL_TRIANGLE_STRIP_ADJACENCY
74 0