1. 光照

显示世界中,光照环境往往是相对复杂的。因为假设太阳作为世界的唯一光源,那么太阳光照在物体A上A将阳光进行反射后,A又做为一个新的光源共同作用于另一个物体B。所以于B来讲光源是复杂的。然而这只是其中一个因素,受制于天气、温度等其他情况我们需要考虑的因素更多。在OpenGL中我们仅考虑一些简单的模型,对现实情况进行一个近似的模拟。这一节中我们主要介绍一下冯氏光照模型。冯氏光照模型中主要有3个分量构成:

  • 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
  • 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
  • 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

接下来我们将逐一讲解这三个分量。

2. 环境光照

上文中提到了,真实世界中的光源情况是复杂的。如果对系统中的每一个光源都进行考虑,这种算法叫做全局照明算法。但是这种算法既开销高昂又极其复杂。我们使用环境光照来简化这个概念,即使用一个很小的常量光照颜色,叠加到实际光照颜色中。

我们看到此处直接用环境颜色与物体颜色相乘。这是因为当前阶段我们还没有添加光源。

3. 漫反射光照

当光线照射在平面上时会发生镜面反射,这是一个光学常识。但事实上真实世界中的物体表面一般都不是完全的平面而是凹凸不平的(微观上的凹凸不平),此时将发生漫反射。漫反射的结果就是,你不仅可以从光源的镜像角度可以观察到物体,从各个角度你都可以观察得到。不过漫反射的强度区域光源射入平面的夹角有关。通过实验,我们观察到当射入角度与平面法线夹角越小时,漫反射强度越强。

我们要在GL中模拟漫反射效果。从上述的叙述中我们知道,漫反射的关键因素在于法线夹角。那么也就是说,我们在GL中需要两点,第一个是法向量,第二个是光线射入的角度

法向量是相对于平面而言的,于点是没有意义的。而同一点在不同平面中对应的法向量也是不同的。这就决定了我们需要改造我们的顶点数据,传一组法向量给顶点着色器,此外绘制VAO时也不能以EBO绘制,而应该以VBO绘制。

虽然对灯的着色器使用不能完全利用的顶点数据看起来不是那么高效,但这些顶点数据已经从箱子对象载入后开始就储存在GPU的内存里了,所以我们并不需要储存新数据到GPU内存中。这实际上比给灯专门分配一个新的VBO更高效了。

4. 计算漫反射光照

顶点数据中我们传入了法向量,我们还需要一个角度。顶点着色器中我们是可以拿到顶点数据的,这时只要我们拿到光源的位置即可以计算出光的方向了,标准化以后就是方向向量了。光源的位置一般是相对固定的,我们可以用uniform变量在外界对着色器进行赋值。记得将光源坐标和顶点坐标都转化为世界坐标哦,或者保证光源坐标、顶点坐标和法向量在同一坐标系统内也可。法向量和方向向量都记得要标准化,这样他们点乘的结果才是两个向量的夹角余弦值。

最后,如果夹角余弦值小于零,我们认为这时无意义的,他将造成我们的漫反射分量为负值,而真实世界中,仅可能是无影响,而不会是负影响。所以我们小于0时我们取0。

片段着色器中的代码大概是这个样子的:

还有一件事,之前说过,要保证光源坐标、顶点坐标和法向量在同一坐标系统内。一般情况下我们会将他们转化为世界坐标,因为这更符合我们的直觉。我们要把法向量也转换到世界空间内。顶点数据中,你所写的法向量直觉上应该是局部坐标,所以要经过模型矩阵转换。此外,模型矩阵中我们也可能对物体进行非比例变化,这将导致我们的法向量发现也发生改变。所以在把法向量从局部坐标转化至世界坐标时,我们要经过法线矩阵的转换。我们可以通过逆矩阵和转置矩阵来变化模型矩阵。所以顶点着色器中我们的代码大概是这个样子的:

其中inverse()是求逆矩阵函数,transpose是求转置矩阵函数。

5. 镜面光照

那么,最后我们只要再将镜面反射叠加进去就好了。

跟漫反射的原理差不多,我们看到物体的颜色跟物体表面镜面反射的光线与我们观察的视角的夹角有关。当夹角越小时,那么镜面光的影响越大。与我们的效果就是,我们将看到一个高光。

那么我们还是需要两个方向向量,第一个是镜面光的方向向量,第二个是观察角度的方向向量

镜面光的方向我们可以根据光源方向及法向量通过反射计算出来。观察方向则是我们摄像机的位置与观察点所构成的向量。

所以我们的两个向量大概是这个样子的:

我们看到,观察方向需要我们传入一个摄像机位置,我们需要在渲染循环中将摄像机的世界坐标传给片段着色器。

计算反射光时,我们使用的是reflect函数。这个函数需要的是传入一个从光源指向平面的向量以及平面的法向量。这里之所以我们传入-lightDir是因为我们之前计算的lightDir是从平面指向光源的。

这里我们最好给一个镜面强度系数,以免当光源垂直照向平面时,我们的镜面光分量过于明亮。

两个向量获取后我们就可以计算他们的夹角来计算我们的镜面光分量了。

首先我们看镜面系数的计算方式,点乘求夹角余弦值后取大于0的值,然后调用pow函数。pow是取这个系数的指定次幂。我们指定了32,代表我们指定了他的反光度是32。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小。在下面的图片里,你会看到不同反光度的视觉效果影响:

至此我们计算出镜面系数,用光源颜色乘镜面系数乘镜面强度系数后即可获得镜面分量。

最后的最后,我们将环境光照、漫反射光照及镜面光照进行叠加后,与物体颜色相乘,即可获得物体在冯氏光照光源影响下所展示的颜色了。

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

在顶点着色器中实现的冯氏光照模型叫做Gouraud着色(Gouraud Shading),而不是冯氏着色(Phong Shading)。记住,由于插值,这种光照看起来有点逊色。冯氏着色能产生更平滑的光照效果。

OpenGL 基础光照详解的更多相关文章

  1. I2C 基础原理详解

    今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...

  2. python 3.x 爬虫基础---Urllib详解

    python 3.x 爬虫基础 python 3.x 爬虫基础---http headers详解 python 3.x 爬虫基础---Urllib详解 前言 爬虫也了解了一段时间了希望在半个月的时间内 ...

  3. RabbitMQ基础知识详解

    什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取队列中 ...

  4. Nmap扫描教程之基础扫描详解

    Nmap扫描教程之基础扫描详解 Nmap扫描基础扫描 当用户对Nmap工具了解后,即可使用该工具实施扫描.通过上一章的介绍,用户可知Nmap工具可以分别对主机.端口.版本.操作系统等实施扫描.但是,在 ...

  5. jmeter 基础功能详解

    jmeter 基础功能详解 thread group:包含一组线程,每个线程独立地执行测试计划. sampler:采样器,有多种不同的sample实现,用来发起各种请求,如http请求,jdbc请求, ...

  6. hadoop基础-SequenceFile详解

    hadoop基础-SequenceFile详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.SequenceFile简介 1>.什么是SequenceFile 序列文件 ...

  7. Cisco路由技术基础知识详解

    第一部分 请写出568A的线序(接触网络第一天就应该会的,只要你掐过,想都能想出来) .网卡MAC地址长度是(  )个二进制位(16进制与2进制的换算关系,只是换种方式问,不用你拿笔去算) A.12  ...

  8. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  9. # OpenGL常用函数详解(持续更新)

    OpenGL常用函数详解(持续更新) 初始化 void glutInit(int* argc,char** argv)初始化GULT库,对应main函数的两个参数 void gultInitWindo ...

  10. 经典Spring入门基础教程详解

    经典Spring入门基础教程详解 https://pan.baidu.com/s/1c016cI#list/path=%2Fsharelink2319398594-201713320584085%2F ...

随机推荐

  1. tensorflow.js 视频图片多目标检测

    前言: Tensorflow.js 官方提供了很多常用模型库,涵盖了平时开发中大部分场景的模型.例如,前面提到的图片识别,除此之外还有人体姿态识别,目标物体识别,语音文字等识别.其中一些可能是 Pyt ...

  2. 为react项目添加开发/提交规范(前端工程化、eslint、prettier、husky、commitlint、stylelint)

    因历史遗留原因,接手的项目没有代码提醒/格式化,包括 eslint.pretttier,也没有 commit 提交校验,如 husky.commitlint.stylelint,与其期待自己或者同事的 ...

  3. Nep2023的wp

    0x00 闲言碎语 2023.8.14 记录11-13的紧张刺激.46名结赛. 非常高兴能够参加NepCTF2023,以一个初出茅庐的新人的身份参加.ctf的乐趣在于学习和探索,同时我也有想证明自己的 ...

  4. django配置swagger自动生成接口文档以及自定义参数设置

    首先安装swagger所用的包 pip install drf-yasg 然后再settings.py 中注册app 接口采用的token认证,在settings.py配置认证方式 SWAGGER_S ...

  5. Pandas 使用教程 CSV

    CSV(Comma-Separated Values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本). CSV 是一种通用的.相对简单的文 ...

  6. 如何通过API接口获取淘宝的店铺所有商品详情

    在电子商务领域中,淘宝是亚洲最大的在线交易平台之一,拥有海量的商品资源和消费者.如果你是一名开发者,想要在自己的网站或者APP中嵌入淘宝商品资源,那么你就需要通过淘宝开放平台提供的API接口来获取这些 ...

  7. Graphviz入门

    Graphviz可以用于状态机图的绘制 要绘制一张状态图,我们需要两个图形元素 结点,边 结点和边都有自己的属性 结点可以是圆.矩形.填充 边有粗细

  8. Ubuntu SVN服务端安装方法

    Ubuntu SVN服务端安装方法:https://blog.csdn.net/sm_wang/article/details/78656120https://www.cnblogs.com/myme ...

  9. 如何使用Python进行投资收益和风险分析

    如何投资是现代企业.个人投资者所面临的实际问题,投资的目标是收益尽可能大,但是投资往往伴随着风险,如果在保证收益最大化的情况下,风险最小:或是风险相同的情况下,如何实现收益的最大化:通过本实训,可以使 ...

  10. ES13 中11个令人惊叹的 JavaScript 新特性

    前言 与许多其他编程语言一样,JavaScript 也在不断发展.每年,该语言都会通过新功能变得更加强大,使开发人员能够编写更具表现力和简洁的代码. 小编今天就为大家介绍ES13中添加的最新功能,并查 ...