QT+OpenGL光照2

news2024/11/18 1:38:31

QT+OpenGL材质

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

材质

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

在这里插入图片描述

在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);
}

整体显示会非常亮
在这里插入图片描述

添加材质,并且改变颜色

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;
}; 

平行光

在这里插入图片描述

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

struct Light {
    // vec3 position; // 使用定向光就不再需要了
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
  vec3 lightDir = normalize(-light.direction);
  ...
}

点光源

在这里插入图片描述

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

衰减:

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

衰减公式如下:
F a t t = 1.0 K c + K l ∗ d + K q ∗ d 2 F_{att} = \frac{1.0}{K_c+K_l*d +K_q *d^2} Fatt=Kc+Kld+Kqd21.0
在这里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);
}

聚光

在这里插入图片描述

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);
    }

}

在这里插入图片描述

聚光边缘太过生硬,因此需要对边缘进行平滑软化;公式如下
I = θ − γ ϵ \begin{equation} I = \frac{\theta - \gamma}{\epsilon} \end{equation} I=ϵθγ

在这里插入图片描述

多光源

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

#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代码。如果您不能运行,可以联系私信博主咨询。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IO模型--从BIO、NIO、AIO到内核select、poll、epoll剖析

IO基本概述 IO的分类 IO以不同的维度划分,可以被分为多种类型;从工作层面划分成磁盘IO(本地IO)和网络IO; 也从工作模式上划分:BIO、NIO、AIO;从工作性质上分为阻塞式IO与非阻塞式IO&#xff1b…

低代码/零代码的快速开发框架

目前国内主流的低代码开发平台有:宜搭、简道云、明道云、云程、氚云、伙伴云、道一云、JEPaaS、华炎魔方、搭搭云、JeecgBoot 、RuoYi等。这些平台各有优劣势,定位也不同,用户可以根据自己需求选择。 一、阿里云宜搭 宜搭是阿里巴巴集团在20…

分布式文件存储Minio学习入门

文章目录一、分布式文件系统应用场景1. Minio介绍Minio优点2. MinIO的基础概念、3. 纠删码ES(Erasure Code)4. 存储形式5. 存储方案二、Docker部署单机Minio三、minio纠删码模式部署四、分布式集群部署分布式存储可靠性常用方法冗余校验分布式Minio优势运行分布式minio使用dock…

如何设置股票接口版交易软件的指标涨跌家数?

如何设置股票接口版交易软件指标涨跌家数?今天小编就以通达信为例给大家介绍一下,很多人其实不知道通达信里面有个很厉害的股票情绪的指标,叫做通达信涨跌家数,打开在通达信软件k线界面,然后输入880005就可以找到了。下…

如何解决 Python 中 TypeError: unhashable type: ‘dict‘ 错误

Python “TypeError: unhashable type: ‘dict’ ” 发生在我们将字典用作另一个字典中的键或用作集合中的元素时。 要解决该错误,需要改用 frozenset,或者在将字典用作键之前将其转换为 JSON 字符串。 当我们将字典用作另一个字典中的键时&#xff0c…

AnlogicFPGA-IO引脚约束设置

(https://www.eefocus.com/article/472120.html此链接是一篇关于XillinxFPGA的IO的状态分析,希望自己也要能了解到AnLogic的IO状态并有对此问题的分析能力) 1、DriveStrength: 驱动强度,即最大能驱动的电流大小(见带负…

Project Caliper:目标是打造最佳VR手柄

一提到Valve Index,人们很快联想到它的五指追踪VR手柄,这款支持手势追踪和体感反馈的高端VR手柄,是市面上最强大的C端VR手柄之一。尽管如此,它依然存在许多缺陷,比如配备的小型摇杆质量不佳、集成式设计不利于维修、人…

算法问题——排序算法问题

摘要 查找和排序算法是算法的入门知识,其经典思想可以用于很多算法当中。因为其实现代码较短,应用较常见。所以在面试中经常会问到排序算法及其相关的问题。但万变不离其宗,只要熟悉了思想,灵活运用也不是难事。一般在面试中最常…

布林线(BOLL)计算公式详解,开口收口代表什么

布林带,英文名称BOLL,是John Bollinger在上世纪八十年代创建的,由中轨、上轨、下轨三条线组成。 一、布林线计算公式详解 布林线中轨是简单移动平均线,一般软件上自带的布林带中轨是20日均线,上轨是中轨加上2个标准差…

Spring 系列之FrameWork

Spring 系列文章 文章目录Spring 系列文章前言一、Spring 介绍二、Spring 架构特征三、Spring 优势四、Spring 体系结构五、IOC 控制反转1. 概念引入2. 原理分析六、Bean 管理1. 介绍2. 管理的内容3. Bean 管理方式1. XML实现DI 赋值2. Bean生命周期1. 测试生命周期2. 后置处理…

RuoYi-Vue搭建(若依)

项目简介 RuoYi-Vue基于SpringBootVue前后端分离的Java快速开发框架1.前端采用Vue、Element UI2.后端采用Spring Boot、Spring Security、Redis & Jwt3.权限认证使用Jwt,支持多终端认证系统4.支持加载动态权限菜单,多方式轻松权限控制5.高效率开发&a…

27岁想转行IT,还来得及吗?

来不来得及不还是看你自身的意愿和条件,这个问题要问你自己吧! 每个人的能力、看法都不同。面对类似的问题,很多人会把侧重点放在IT上,或者27岁上面。那么我们试着换一个方式来问呢:什么时候适合转行,有哪些…

1.PostgreSQL

文章目录LIMITWITH 和RECURSIVEPostgreSQL 约束PostgreSQL AUTO INCREMENT(自动增长)PostgreSQL PRIVILEGES(权限)GRANT语法LIMIT SELECT * FROM COMPANY LIMIT 3 OFFSET 2;WITH 和RECURSIVE WITH RECURSIVE t(a,b) AS (VALUES (…

每日学术速递2.16

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Efficient Teacher: Semi-Supervised Object Detection for YOLOv5 标题:高效教师:YOLOv5 的半监督目标检测 作者:Bowen Xu, Mingtao Chen, Wen…

用ChatGPT来预测2023美国大学生数学建模赛题以及高分攻略

一.2023年美赛 2023年美赛将于2023年2月17-21日举行,将会在北京时间:2023年2月 21日,上午10:00前提交论文。 二.用ChatGPT来预测2023美国大学生数学建模赛题 2.1预测一下会有什么类型的题目 答: 由于我是一个人工…

AcWing语法基础课笔记 第五章 C++中的字符串

第五章 C中的字符串 字符串是计算机与人类沟通的重要手段。 ——闫学灿 字符与整数的联系——ASCII码 每个常用字符都对应一个-128~127的数字,二者之间可以相互转化: 常用ASCII值:’A’-‘Z’ 是65~90,’a’-‘z’…

【docker知识】DockerFile语法 2:构造指令

1官方文档:Dockerfile reference | Docker Documentation 一、说明 我们将以HelloWorld案例的方法,由浅入深地理解DockerFile指令,并生成自己的镜像。为了避免冗长的官网文章污染您的视线,这里将重要的,常见的指令…

Springcloud-配置中心config

一、添加依赖<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId&…

【大数据】HADOOP-YARN容量调度器多队列配置详解实战

简介 Capacity调度器具有以下的几个特性&#xff1a; 层次化的队列设计&#xff0c;这种层次化的队列设计保证了子队列可以使用父队列设置的全部资源。这样通过层次化的管理&#xff0c;更容易合理分配和限制资源的使用。容量保证&#xff0c;队列上都会设置一个资源的占比&a…

数据分析与SAS学习笔记4

INPUT语句&#xff1a;格式修饰符&#xff1a; “:” 修饰符。表示从下一个非空格列读入数据&#xff0c;直到:1 遇到再下一个空格列&#xff1b; 2 读到预先定义的变量长度&#xff1b; 3 数据行结束。哪个先出现就在哪儿结束。 “&” 修饰符。表示从下一个非空格列读入…