第七章 光照

如图7.1左边,一个没加光照的球体,右边一个加光照的球体。可见,左边的球看起来是平的 - 也许它根本不是一个球,而只是一个二维圆。另一方面,右边的球体看上去是3D的-照明和阴影帮助我们感知物体的形状和体积。事实上,我们对世界的视觉感受依赖于光及其与物质的相互作用,因此,产生真实照片的场景的许多问题都与物理上精确的照明模型有关。当然,模型越精确,计算量越大; 因此必须权衡效果和速度。如,用于电影...
摘要由CSDN通过智能技术生成

如图7.1左边,一个没加光照的球体,右边一个加光照的球体。可见,左边的球看起来是平的 - 也许它根本不是一个球,而只是一个二维圆。另一方面,右边的球体看上去是3D的-照明和阴影帮助我们感知物体的形状和体积。事实上,我们对世界的视觉感受依赖于光及其与物质的相互作用,因此,产生真实照片的场景的许多问题都与物理上精确的照明模型有关。

当然,模型越精确,计算量越大; 因此必须权衡效果和速度。如,用于电影的3D特效场景十分复杂,并且使用非常逼真的照明模型,因为电影的帧是预先渲染的,所以他们可以花费数小时或数天来处理帧。游戏, 另一方面是实时应用,因此,帧需要以至少30帧/秒的速率绘制。

注意,本书中解释和实现的照明模型主要基于[Möller02]中描述的模型。

目标:
1.对灯光和材料之间的相互作用有一个基本的了解。
2.了解局部光照与全局光照的区别。
3.掌握我们如何用数学方法描述表面上的一个点的方向是“面向”,以便我们可以确定入射光照到表面的角度。
4.学习如何正确转换法向量。
5.能够区分环境光,漫射光和镜面光。
6.学习如何实现定向灯,点灯和聚光灯。
7.了解如何通过控制衰减参数来改变作为深度函数的光强度。

7.1光和材料的相互作用

使用照明时,不再直接指定顶点颜色; 而是指定材质和灯光,然后应用一个光照方程,根据光线/材料的相互作用计算出顶点颜色。这使对象的更真实的着色(比较图7.1a和7.1b)。

7-1
图7.1 (a)未加光照的球体看起来是二维的 (b)加光照的球体看起来是3D的

材质可以被认为是决定光线如何与物体表面相互作用的属性。例如,表面反射和吸收的光的颜色,反射率,透明度和光泽度都是组成表面材料的参数。然而在本章中,我们只关心表面反射和吸收的光线的颜色,以及光泽度。

在我们的模型中,光源可以发出不同强度的红光,绿光和蓝光; 通过这种方式,我们可以模拟出多种颜色的光。当光线从光源向外传播并与物体发生碰撞时,一些光线可能被吸收,一些光线可能被反射(一些光线会通过透明物体,如玻璃,但我们在这里不考虑透明度)。反射光现在沿着新的路径传播,并可能撞击其他物体,在这些物体上再次吸收和反射一些光线。光线在被完全吸收之前可能撞击许多物体。一些光线最终会进入眼睛(见图7.2),并撞击视网膜上的感光细胞(称为视锥细胞和视杆细胞)。

7-2
图7.2 (a)入射白光的通量。 (b)光照射在圆柱体上,一些光线被吸收,其他光线被散射到眼睛和球体上。(c)从圆柱体向球体反射的光被再次吸收或反射并传播到眼睛中。(d)眼睛接收到的光线决定了眼睛看到的东西。

根据三色理论(见[Santrock03]),视网膜包含三种感光细胞,分别对应红,绿和蓝色光敏感(有一些重叠)。输入的RGB光根据光的强度以不同的强度刺激其相应的光感细胞。 当感光细胞被刺激(或不受刺激)时,神经冲动由视神经传给大脑,大脑根据光受体的刺激在脑中产生图像。(当然,如果你闭上你的眼睛,受体细胞就不会受到刺激,大脑就会将其记录为黑色。)

例如,再次考虑图7.2。假设圆柱体的材料反射75%的红光,75%的绿光,吸收其他所有光,球体反射25%的红光,吸收其他所有光。还假设从光源发出纯白光。当光线撞击圆柱体时,所有的蓝光都被吸收,只有75%的红光和绿光被反射(即中等强度的黄光)。这些光线是散射的 - 有些光线会传播到眼睛,有些则会传播到球体。进入眼睛的部分以一半强度主要刺激红色和绿色的锥形细胞;因此,观察者将该圆柱视为黄色的半明亮影像。现在,其他光线向球体传播并撞击。球体反射25%红光,吸收其余部分;因此稀释的入射红光(中高强度红光)被进一步稀释并反射,并且所有入射的绿光都被吸收。这剩余的红光然后进入眼睛,主要刺激红色的视锥细胞到一个较低的程度。因此,观众看到球体是一个深红的阴影。

和大多数实时应用一样,在本书中采用的照明模型被称为局部照明模型。在局部模型中,每个物体独立于另一个物体而被照亮,并且在照明过程中仅考虑从光源直接发出的光(即,从其他场景物体弹起后打在当前被照亮的物体的光是忽略)。图7.3显示了这个模型的一个结果。

相反,全局照明模型不仅考虑从光源直接发出的光线,还考虑到从场景中的其他物体反射回来的间接光线。 这些被称为全局照明模型,因为它们在照亮物体时考虑全局场景中的所有事物。全局照明模式通常对于实时游戏而言过于昂贵(但是非常接近于产生照片真实感的场景)。实时全局照明方法正在进行研究。

7-3
图7.3 在物理上,墙壁阻挡了灯泡发出的光线,球体在墙的阴影中。 然而,在一个局部照明模型中,球体像墙壁不在那里一样点亮

7.2 法向量

面法线是描述多边形面向的方向(即,与多边形上的所有点正交)的单位向量; 见图7.4a。表面法线是与表面上的点的切平面正交的单位矢量;请参见图7.4b。观察表面法线确定表面上的一个点“面对”的方向。

对于照明计算,我们需要每个三角形网格定点的表面法线,以便我们可以确定光照在网格表面上点的角度。为了获得曲面法线,我们仅在顶点处(即所谓的顶点法线)指定曲面法线。然后,为了在三角形网格的表面上的每个点上获得曲面法线近似,这些顶点法线将在光栅化期间在三角形内插(参见§5.10.3和图7.5)。

NOTE:对每个像素的法线进行插值计算称为像素照明或phong照明。 一个便宜但不太精确的方法是在每个顶点进行照明计算。 然后,从顶点着色器输出每个顶点光照计算的结果,并在三角形的像素内插。 从像素着色器到顶点着色器的移动计算是一个常见的性能优化的质量,有时视觉差异是非常微妙的,使这种优化非常有吸引力。

7-4
图7.4 (a)面法线正交于面上的所有点。 (b)曲面法线是与曲面上的一个点的切平面正交的向量。

7-5
图7.5 顶点法线n0和n1在分段顶点点p0和p1处定义。线段内部的点p的法线向量n通过顶点法线之间的线性内插(加权平均)来找到; 即n = n0 + t(n1-n0),其中t是使得p = p0 + t(p1-p0)。尽管为了简单起见,我们在一个线段上说明了正常的插值,但是这个想法直接推广到在三角形上进行插值。

7.2.1计算法向量

为了找到一个三角形 Δp0p1p2 Δ p 0 p 1 p 2 的面法线,我们首先计算两个位于三角形边缘上的向量:

u=p1p0v=p2p0 u = p 1 – p 0 v = p 2 – p 0

那么面向量为:
n=u×v||u×v|| n = u × v | | u × v | |

以下是从三角形的三个顶点计算三角形正面(§5.10.2)的面法线的函数。

void ComputeNormal(const D3DXVECTOR3& p0,
                const D3DXVECTOR3& p1,
                const D3DXVECTOR3& p2,
D3DXVECTOR3& out)
{
    D3DXVECTOR3 u = p1 - p0;
    D3DXVECTOR3 v = p2 - p0;
    D3DXVec3Cross(&out, &u, &v);
    D3DXVec3Normalize(&out, &out);
}

对于可微分曲面,我们可以使用微积分来找到曲面上的点的法线。不幸的是,三角网格是不可微分的。通常应用于三角形网格的技术称为顶点法线平均。通过对共享顶点v的网格中的每个多边形的面法线进行平均来找到网格中任意顶点v的顶点法线n。例如,在图7.6中,网格中的四个多边形共享顶点v; 因此,v的顶点法线由下式给出:

narg=n0+n1+n2+n3||n0+n1+n2+n3|| n a r g = n 0 + n 1 + n 2 + n 3 | | n 0 + n 1 + n 2 + n 3 | |

7-6
图7.6 中间顶点由相邻的四个多边形共享,所以我们通过对四个多边形面法线进行平均来逼近中间顶点法线。

在前面的例子中,我们不需要像典型的平均值那样除以4,因为我们将结果归一化。还要注意,可以构建更复杂的平均方案; 例如,可以在权重由多边形的面积确定的情况下使用加权平均(例如,具有较大面积的多边形具有比具有较小面积的多边形更多的权重)。

下面的伪代码展示了如何在给定三角形网格的顶点和索引列表的情况下实现这种平均:

// Input:
// 1. An array of vertices (mVertices). Each vertex has a
// position component (pos) and a normal component (normal).
// 2. An array of indices (mIndices).

// For each triangle in the mesh:
for(UINT i = 0; i < mNumTriangles; ++i)
{
    // indices of the ith triangle
    UINT i0 = mIndices[i*3+0];
    UINT i1 = mIndices[i*3+1];
    UINT i2 = mIndices[i*3+2];
    // vertices of ith triangle
    Vertex v0 = mVertices[i0];
    Vertex v1 = mVertices[i1];
    Vertex v2 = mVertices[i2];
    // compute face normal
    Vector3 e0 = v1.pos - v0.pos;
    Vector3 e1 = v2.pos - v0.pos;
    Vector3 faceNormal = Cross(e0, e1);
    // This triangle shares the following three vertices,
    // so add this face normal into the average of these
    // vertex normals.
    mVertices[i0].normal += faceNormal;
    mVertices[i1].normal += faceNormal;
    mVertices[i2].normal += faceNormal;
}
// For each vertex v, we have summed the face normals of all
// the triangles that share v, so now we just need to normalize.
for(UINT i = 0; i < mNumVertices; ++i)
    mVertices[i].normal = Normalize(&mVertices[i].normal));

7.2.2法向量的转换

考虑图7.7a,其中我们有一个与法向量n正交的切向量 u=v1v0 u = v 1 − v 0 。 如果我们应用非均匀缩放变换A,我们从图7.7b可以看出,变换后的切向量 uA=v1Av0A u A = v 1 A − v 0 A 并不保持与变换的法向量nA正交。

所以我们的问题是这样的:给定一个变换点和向量(非正态)的变换矩阵A,我们想要找到一个变换矩阵B来变换法向量,使得变换后的切向量与变换的法向量正交(即, uA·nB = 0)。 要做到这一点,首先让我们从我们知道的事情开始:我们知道法向量n正交于切向量u则有:

u · n =0 法向量与切向量正交
unT = 0 将点积写为矩阵乘法
u(AA-1) nT = 0 插入单位矩阵I=AA-1
(uA)(A–1 nT) = 0 矩阵乘法结合律
(uA) ((A–1 nT)T)T = 0 转置属性(AT)T=A
(uA) (n(A–1)T)T = 0 转置属性(AB)T=BTAT
uA · n(A–1)T = 0
uA · nB = 0 转换后的正切与方向向量正交

因此,B =(A-1)T(A的逆转置)在转换法向矢量方面做了工作,以使它们垂直于其相关的变换的切向量uA.

7-7
图7.7 (a)转换前的表面正常。 (b)在x轴上缩放2个单位后,法线不再与表面垂直。 (c)通过缩放变换的逆转置正确变换的曲面法线。

注意如果矩阵是正交的 AT=A1 ( A T = A − 1 ) ,那么B =(A^{-1})^T =(A^T)^T = A; 也就是说,我们不需要计算逆转置,因为A在这种情况下完成了工作。 总之,当通过非均匀或剪切变换来变换法向矢量时,使用逆转置。

我们在MathHelper.h中实现一个辅助函数来计算逆转置:

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

我们从矩阵中清除任何翻译,因为我们使用逆转置来转换向量,翻译仅适用于点。 然而,从§3.2.1我们知道设置w = 0的矢量(使用齐次坐标)防止了矢量被翻译修改。 因此,我们不需要将矩阵中的翻译归零。 问题是如果我们想连接逆转置和不包含非均匀缩放的另一个矩阵,比如视图矩阵(A-1TV,(A-1T“第4列中的转置转换泄漏” 进入产品矩阵造成错误。 因此,为了避免这种错误,我们将翻译作为预防措施进行归零。 正确的方法是用((AV)-1)T来转换法线。以下是一个缩放和平移矩阵的例子,以及第四列不是[0,0,0,1]的逆转置看起来如何:

A=100100.501000.510001(A1)T=100100.501000.5
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值