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

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、光照模型的概述

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

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

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

posted @ 2019-11-19 21:49  风雅yaya  阅读(359)  评论(0编辑  收藏  举报