8、光照

学习目标

  1. 对光照和材质的交互有基本的了解
  2. 了解局部光照和全局光照的区别
  3. 探究如何用数学来描述位于物体表面上某一点的“朝向”,以此来确定入射光照射到表面的角度
  4. 学习如何正确的变换法向量
  5. 能够区分环境光、漫反射光以及镜面光
  6. 学习如何实现平行光、点光以及聚光灯
  7. 理解如何通过控制距离函数的衰减参数来实现不同的光照强度

8.1、光照和材质的交互

开启光照之后,我们不需要指定顶点的颜色,而是指定材质和光照,然后运用光照方程基于两者的交互来计算出顶点的颜色。我们可以把材质看作是确定光照和物体表面如何进行交互的属性集,在这个属性集合里的属性有:

  1. 表面反射光和吸收光的颜色
  2. 表面下的材质的折射率
  3. 表面的光滑度以及透明度

通过指定材质属性,我们就能为真实世界的物体表面进行建模。

根据三色论,视网膜含有三种光受体,分别对红、绿、蓝三色光敏感,RGB入射光刺激视网膜相应的光受体,基于光线的强度产生不同程度的刺激,光受体受到刺激(或没有受到刺激)将神经冲动沿着视觉神经传到大脑,然后大脑根据光受体产生的刺激在人脑里生成对应的画面(如果我们把眼睛闭上,受体细胞不会受到刺激,因此大脑会产生黑色的画面)

在本书中,我们所采用的光照模型都是局部光照模型,该模型中每个物体的光照都独立于其他物体,我们在处理光照的过程中只需要考虑光源直接发射出来的光线,不需要考虑其他物体反射的光线

如果使用全局光照模型,我们除了要考虑光源直接发射出来的光之外,还要考虑其他物体反射的光线,这会造成极大的游戏开销,一般是实时游戏负担不起的,但是全局光照模型可以呈现出接近真实世界的场景,所以接近于全局光照的实时方法一直处于研究阶段。

8.2、法向量

平面法线(法向量)是一种描述多边形朝向(正交于多边形上的所有点)的单位向量。曲面法线是一种垂直于曲面上一点处切平面的单位向量。

对于光照计算来说,我们需要通过三角形网格曲面上每一点处的曲面法线来确定光线照射到对应点的角度,为了求出每一个点的曲面法线,我们需要先求出每一个顶点的曲面法线(顶点法线),然后再光栅化过程中对这些顶点法线进行插值计算,以此求出三角形网格每一个点处的近似曲面法线。

8.2.1、计算法向量

为了找到三角形ABC的平面向量,我们首先计算三角形边上的两个向量

\[ u = A - B
\]
\[ v = A - c
\]

那么这个三角形的平面法线是:

\[ n = (u 叉乘 v)/||(u 叉乘 v)||
\]

下面函数将根据三角形的三个顶点计算该三角形正面的平面法线:

XMVECTOR ComputeNormal(FXMVECTOR A, FXMVECTOR B, FXMVECTOR C)
{
XMVECTOR u = A - B;
XMVECTOR v = A - C;
return XMVector3Normalize(XMVector3Cross(u, v));
}

三角形网格使用一种称为求顶点法线平均值的计算方法来求取曲面法线,这种方法通过对网格中共享顶点v的多边形的平面法线求取平均值,从而获取网格中任意顶点v处的顶点法线n。如果想要得到更加精确的结果,我们可以使用更复杂的求平均值的方法,比如根据多边形的面积来确定权重,以求取加权平均值。

下面伪代码展示了如何求取法线平均值:

	//对于网格中的每一个三角形来说
for (UINT i = 0; i < mNumTriangles; ++i)
{
//第i个三角形的索引
UINT i0 = mIndices[i * 3 + 0];
UINT i1 = mIndices[i * 3 + 1];
UINT i2 = mIndices[i * 3 + 2]; //第i个三角形的顶点
Vertex v0 = mVertex[i0];
Vertex v1 = mVertex[i1];
Vertex v2 = mVertex[i2]; //计算平面法线
Vector3 e0 = v1.Pos - v0.Pos;
Vector3 e1 = v1.Pos - v2.Pos;
Vector3 faceNoraml = Cross(e0, e1); //该三角形共享了3个顶点,所以将这个平面法线和这些顶点法线相加以求取平均值
mVertex[i0].Normal += faceNoraml;
mVertex[i1].Normal += faceNoraml;
mVertex[i2].Normal += faceNoraml;
} //对于每一个顶点v来说,我们已经对所有共享顶点v的三角形平面法线进行了求和,
//所以我们现在只要进行规范化处理就可以了
for (UINT i = 0; i < mVertexNum; ++i)
{
mVertex[i].Normal = Normalize(&mVertex[i].Normal);
}

8.2.2、变换法向量

思考,如图,切向量u和法向量n相互正交,如果对此应用一个非等比缩放变换A,则从图中可以看出,变换后的切向量和法向量不能继续保持正交关系,所以,我们现在面对的问题是:若给定一个用于变换点和向量(非法线)的变换矩阵A,如何能求处一个变换矩阵B,通过B来变换法向量,使经矩阵A变换后的点或向量可以继续和法向量保持正交关系。

图片为手机拍摄,望见谅(图片来源:DirectX 12 3D 游戏开发实战)

这里直接给出结论:B = A的逆转置矩阵

在Mathhelper.h文件中,有一个计算逆转置矩阵的辅助函数:

	static DirectX::XMMATRIX InverseTranspose(DirectX::CXMMATRIX M)
{
DirectX::XMMATRIX A = M;
A.r[3] = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f); DirectX::XMVECTOR det = DirectX::XMMatrixDeterminant(A);
return DirectX::XMMatrixTranspose(DirectX::XMMatrixInverse(&det, A));
}

在通过逆转置矩阵进对向量进行变换时,我们可以将向量变换矩阵中和平移操作有关的项清零,从而只允许点才可以进行平移操作(因为向量的性质中没有位置这个概念)。

补充:尽管使用了逆转置矩阵对法向量进行变换,但是法向量任然可以会失去单位长度,所以变换完成之后我们需要对法向量进行规范化处理以防万一。

8.3、参与光照计算的一些关键向量

8.4、朗伯余弦定律(发光强度)

光源每一秒发出的能量称为辐射通量,单位面积上的辐射通量密度称为辐射照度。我们将使用辐射照度来确定某区域所接收的光量(即眼睛感受到的明亮度)。(我们可以将辐射照度视为照射到表面某一区域的光量)

光线垂直照射到物体表面的光照强度要大于非垂直照射的光照。如果有一束辐射通量为P,横截面积为A的光束,如果将这束光照射到物体表面上的面积为A,则A内的辐射照度为:E = P/A。如果转动光源,使它非垂直照射到物体表面,则此时光照将覆盖物体上更多的面积B(垂直照射到物体的光,只有面积为A才会被光照射到。显然斜着照射可以使更大的面积接收到光照)。该面积的辐射照度为:E1 = P / B;

\[ A和B的关系为:cosx = A / B,X为入射光和垂直物体表面的法线
\]
\[ 所以:E1 = Ecosx = E(n,L),n为物体表面的法向量,L为光向量
\]

这就是朗伯余弦定律:面积B内的辐射照度就相当于将受垂直方向光照的面积A内的辐射照度按比例nL = cosx进行缩放(看不懂这句话没关系,看懂这句话上面的内容就可以了)

8.5、漫反射光照

漫反射的定义:当光线照射到不透明的物体表面时,一部分光会进入物体的内部,并与表面附近的物质相互作用。这些光会在物体内部四处反弹,一部分会被吸收,余下的部分则会向各个方向散射并返回表面,这就是漫反射

漫反射光照的计算并不复杂:首先我们要指定光照颜色以及漫反射反照率颜色,漫反射反照率表示根据表面的漫反射率而被反射的入射光量。然后使用分量式颜色乘法对他们进行处理,经过分量式颜色乘法处理之后并不准确,我们还需要考虑物体表面实际接收到的原始光量,所以要加上朗伯余弦定律

\[ c = max(L * n,0)*B*m;
\]

B为入射光量,m为漫反射反射率,L为光向量,n为表面法线,c为反射光量

8.6、环境光照

如前文所述,我们使用的是局部光照模型,不会考虑其他物体反射过来的间接光照。然而在真实世界中,我们看见的绝大多数都是间接光,所以为了使光照效果更加贴近生活,我们给光照方程引进了环境光

\[ c = A * m
\]

A指定了颜色接收到的间接光量,m指定了根据表面漫反射率而被表面反射的入射光量,c为环境光量。

补充:这里的环境光并不是根据真实的物理世界计算出来的反射光,而是以统一的亮度将物体稍微照亮一点点。这个模型的总体思路是:间接光照在场景中会发生多次的反射和散射,并会在所有方向上均等的射向目标物体

8.7、镜面光照

在8.5节,我们使用漫反射光照来模拟漫反射的过程:光进入介质,部分光被吸收,而剩下的光则向介质外的各个方向进行散射。现在我们要介绍的是根据一种名为菲涅尔效应的物理现象而产生的光照。

当光线到达两种不同的折射率(光在真空中传播的速度和光在给定介质中的传播速度之比)介质之间的界面时,一部分关会被反射,而剩下的光会发生折射,我们把这种光的反射过程称为镜面反射,把被反射的光称为镜面反射光

如果折射光沿折射向量从介质的另一侧射出,并进入观察者的眼中,那么该物体看起来就像时透明的,在实时的图像处理中,一般用alpha混合技术或后期处理特效来模拟透明对象的折射过程。相关的知识我们将会在后续进行介绍。

从表面反射和进入眼睛的光量时由漫反射光和镜面光组成的,和漫反射光相比,镜面光可能不会进入眼睛,因为镜面反射只发生在某一特定的角度上。

8.7.1、菲涅尔效应

略(以数学方法描述入射光线被反射的百分比)

8.7.2、表面粗糙度

8.8、光照模型的概述

现在我们将所有的光照内容总结起来,即物体表面反射的光量相当于环境反射光、漫反射光以及镜面反射光的总和

光照类型 作用
环境光 模拟经物体表面反射的间接光量
漫反射光 对进入介质内部,又经过表面下吸收而最终散射出来的光进行模拟
镜面反射光 模拟菲涅尔效应和表面粗糙度共同作用而形成反射光

所以本书的光照方程 = 环境光 + 漫反射光 + 镜面反射光

DirectX12 3D 游戏开发与实战第八章内容(上)的更多相关文章

  1. DirectX12 3D 游戏开发与实战第八章内容(下)

    DirectX12 3D 游戏开发与实战第八章内容(下) 8.9.材质的实现 下面是材质结构体的部分代码: // 简单的结构体来表示我们所演示的材料 struct Material { // 材质唯一 ...

  2. DirectX12 3D 游戏开发与实战第九章内容(上)

    仅供个人学习使用,请勿转载. 9.纹理贴图 学习目标: 学习如何将局部纹理映射到网格三角形上 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次纹理贴图 ...

  3. DirectX12 3D 游戏开发与实战第一章内容

    DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...

  4. DirectX12 3D 游戏开发与实战第二章内容

    矩阵代数 学习目标 理解矩阵及其相关运算的定义 探究为何能把向量和矩阵的乘法视为一种线性组合 学习单位矩阵.转置矩阵.行列式以及矩阵的逆等概念 逐步熟悉DirectXMath库中提供的关于矩阵计算的类 ...

  5. DirectX12 3D 游戏开发与实战第十章内容(下)

    仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...

  6. DirectX12 3D 游戏开发与实战第十章内容(上)

    仅供个人学习使用,请勿转载.谢谢! 10.混合 本章将研究混合技术,混合技术可以让我们将当前需要光栅化的像素(也称为源像素)和之前已经光栅化到后台缓冲区的像素(也称为目标像素)进行融合.因此,该技术可 ...

  7. DirectX12 3D 游戏开发与实战第九章内容(下)

    仅供个人学习使用,请勿转载.谢谢! 9.纹理贴图 学习目标 学习如何将局部纹理映射到网格三角形中 探究如何创建和启用纹理 学会如何通过纹理过滤来创建更加平滑的图像 探索如何使用寻址模式来进行多次贴图 ...

  8. DirectX12 3D 游戏开发与实战第五章内容

    渲染流水线 学习目标: 了解用于在2D图像中表现出场景立体感和空间深度感等真实效果的关键因素 探索如何用Direct3D表示3D对象 学习如何建立虚拟摄像机 理解渲染流水线,根据给定的3D场景的几何描 ...

  9. DirectX12 3D 游戏开发与实战第四章内容(上)

    Direct3D的初始化(上) 学习目标 了解Direct3D在3D编程中相对于硬件所扮演的角色 理解组件对象模型COM在Direct3D中的作用 掌握基础的图像学概念,例如2D图像的存储方式,页面翻 ...

随机推荐

  1. 【UE4 设计模式】建造者模式 Builder Pattern

    概述 描述 建造者模式,又称生成器模式.是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 建造者模式将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无需知道复杂 ...

  2. 人人都写过的5个Bug!

    大家好,我是良许. 计算机专业的小伙伴,在学校期间一定学过 C 语言.它是众多高级语言的鼻祖,深入学习这门语言会对计算机原理.操作系统.内存管理等等底层相关的知识会有更深入的了解,所以我在直播的时候, ...

  3. Maven还停留在导jar包?快来探索Nexus私服的新世界

    写在前面 Maven,学习框架之前我们都会接触到的一个工具,感觉他的定位,似乎就跟git一样,只是方便我们开发?于是自然而然的,很多小猿对于Maven都只是停留在会用的阶段,利用他来构建,打包,引入j ...

  4. HTML+CSS基础(HTML篇)

    引言 在日常开发Android中,很多时候会遇到和WebView打交道,对CSS HTML JS不是很清楚的话是完不成一些功能的,本篇开始学习HTML,文章的主要内容是总结了慕课网中,HTML+CSS ...

  5. git commit--fatal: unable to auto-detect email address

    git commit的时候报错 *** Please tell me who you are. Run git config --global user.email "you@example ...

  6. 数字在排序数组中出现的次数 牛客网 剑指Offer

    数字在排序数组中出现的次数 牛客网 剑指Offer 题目描述 统计一个数字在排序数组中出现的次数. class Solution: def GetNumberOfK(self, data, k): i ...

  7. 双栈排序 牛客网 程序员面试金典 C++ Python

    双栈排序 牛客网 程序员面试金典 C++ Python 题目描述 请编写一个程序,按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中. ...

  8. LCA-离线tarjan模板

    /* *算法引入: *树上两点的最近公共祖先; *对于有根树的两个结点u,v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u,v的祖先且x的深度尽可能大; *对于x来说,从u到v的路径一定 ...

  9. GDI+图形图像技术1

    System.Drawing命名空间提供了对GDI+基本图形功能的访问,其中一些子命名空间中提供了更高级的功能. GDI+由GDI发展而来,是Windows图形显示程序与实际物理设备之间的桥梁. GD ...

  10. Windows内核基础知识-5-调用门(32-Bit Call Gate)

    Windows内核基础知识-5-调用门(32-Bit Call Gate) 调用门有一个关键的作用,就是用来提权.调用门其实就是一个段. 调用门: 这是段描述符的结构体,里面的s字段用来标记是代码段还 ...