@[C++]DirectX 12 3D游戏开发实战(DX12龙书)
开始尝试使用CSDN来对每天的学习进行记录,记录内容主要为书中的概念摘录与个人理解,由于本人代码知识比较浅薄,在理解上可能会有偏差,欢迎指正,谢谢!
仅作为个人学习用,请勿转载,谢谢
第8章 光照
光照使我们感受到目标物体的实体形状,展现体积感,视觉上对世界感知依靠光照与其材质的交互。
词汇
光照:lighting 阴影:shading 材质:Material 光照模型:lighting model 光照方程:lighting equation
强度:intensity 介质(媒质):medium 三色论:trichromatic theory 散射:scatter
半光亮:semi-bright 局部光照模型:local illumination model 照明:illumination
全局关照模型:global illumination model 平面法线:face normal 法向量:normal vector
曲面法线:surface normal 顶点法线:vertex normal 逐像素光照:per pixel lighting
逐顶点光照:pre vertex lighting 求顶点法线平均值:vertex normal averaging
切向量:tangent vector 剪切变换:shear transformation
逆:inverse 行列式:determinant 反映:reflect 辐射通量 radiant flux
辐(射)照度:irradiance 漫反射:diffuse reflection 漫反射反照率:diffuse albedo
漫反射率:diffuse reflection 环境光:ambient light 菲涅尔效应:Fresnel effect
折射率:index of refraction 折射:refract 镜面反射:specular reflection 镜面光:specular light
斯涅尔折射定律:Snell’s Law 菲涅尔方程:Fresnel equations
石里克近似法:Schlick approximation 透射光:transmitted light 本体反射率:body reflectance
理想镜面:perfect mirror 粗糙度roughness 微观表面法线:micro-normal
宏观表面法线:macro-normal 镜面瓣:specular lobe 微平面:microfacet
中间向量:halfway vector 光泽度:shininess 粒度:granularity
学习目标
1.对光照与材质的交互有基本的了解。
2.了解局部光照与全局光照之间的差异。
3.探究如何用数学描述位于物体表面上某点的朝向,以此来确定入射光找到表面的角度。
4.学会如何正确的变换法向量。
5.区分环境光/漫反射光/镜面光。
6.学习如何实现平行光/点光/聚光3种光源。
7.理解通过控制距离函数的衰减参数实现不同的光照强度。
内容
8.1光照与材质的交互
1.不再直接指出顶点颜色,而采用指定材质与光照,再运用光照方程基于两者的交互来计算顶点颜色,使物体的颜色更趋于现实。
2.将材质看作确定光照与物体表面如何进行交互的属性集,其中包含:表面反射光和吸收光的颜色、表面下材质的折射率、表面的光滑度及表面的透明度。
3.光源可以发出各种强度的红绿蓝三色混合光来模拟多种光源颜色。
4.光线经过不断反射最终传入观察者眼中并照射到视网膜上的感光细胞。
5.三色论百度百科链接
6.本书中及大多数实时应用程序采用的光照模型为局部光照模型:每个物体的光照皆独立于其他物体,在光照过程中仅考虑光源直接发出的光线(忽略来自其它物体的反射光)。
7.全局光照模型:对一个物体进行照明时考虑整个场景中的所有事物,开销较大,不适合实时游戏,但可以生成接近照片级真实感的场景。
8.接近全局关照的实时方法仍处于研究阶段例如立体像素全局光照(voxel global illumination)技术 参考文献:Practical Real-Time Voxel-Based Global Illumination for Current GPUs
9.另一种方法是预计算静态物体的间接光照,再用得到的结果来近似地模拟动态物体的间接关照
之前在使用UE4时,天真的我以为把全部光源关闭,并且使场景的四周被墙体包围就可以实现一个漆黑的场景,现在想想是不是和局部光照模型的使用有关?
8.2法向量
1.平面法线是一种描述多边形朝向的的单位向量
2.曲面法线是一种垂直于曲面上一点处切平面的单位向量,根据曲面法线即可确定对于曲面上某点的朝向
3.对于光照计算来说,需要通过三角形网格曲面上每一点出的曲面法线来确定光线照到对应点上的角度
4.为了求出曲面法线,我们仅先指定位于网格顶点处的曲面法线(顶点法线),接下来为了取得三角形网格曲面上每个点处的近似曲面法线,在三角形光栅化过程中对这些顶点发现进行插值计算(利用三角形顶点的属性值计算出其内部像素的属性值)
5.逐像素光照(phong关照)模型:对每一个像素逐一进行法线插值并执行光照计算的方法
6.逐顶点光照模型:针对每个顶点逐一进行光照计算的方法,开销低精度也低,接下来顶点着色器输出每个顶点光照的计算结果,再对三角形中的像素进行插值。
7.将运算作业从像素着色器移至顶点着色器是一种常见的性能优化手段,尤其在以质量为重但又允许结果存在少许视觉偏差的情况下
8.2.1计算法向量
1.为找到三角形p0,p1,p2的平面法线,我们首先计算位于三角形边上的两个向量
u = p 1 − p 0 u=p1-p0 u=p1−p0
v = p 2 − p 0 v=p2-p0 v=p2−p0
三角形的平面法线为
n = u ∗ v / ∣ u ∗ v ∣ n=u*v/|u*v| n=u∗v/∣u∗v∣
计算三角形的三个顶点来计算该三角形正面的平面法线函数:
XMVECTOR ComputeNormal(FXMVECTOR p0,FXMVECTOR p1,FXMVECTOR p2)
{
XMVECTOR u = p1 - p0;
XMVECTOR v = p2 - p0;
return XMVector3Normalize(XMVector3Cross(u,v));
}
2.三角形网格使用一种被称为“求顶点法线平均值”的计算方法:通过对网格中共享顶点v的多边形平面法线求取平均值,从而获得网格中任意顶点v处的顶点法线n。
n a v g = ( n 0 + n 1 + n 2 + n 3 ) / ∣ n 0 + n 1 + n 2 + n 3 ∣ navg=(n0+n1+n2+n3)/|n0+n1+n2+n3| navg=(n0+n1+n2+n3)/∣n0+n1+n2+n3∣
3.还可以采取更加复杂的求平均值方法,比如根据多边形面积确定权重以求取加权平均值。
//输入
//1.一个顶点数组(mVertices)。每个顶点都有一个位置分量(pos)和一个法线分量(normal)
//2.一个索引数组(mIndices)
//对于网格中的每个三角形
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 = mVertices[i0];
Vertex v1 = mVertices[i1];
Vertex v2 = mVertices[i2];
//计算平面法线
Vector3 e0 = v1.pos - v0.pos;
Vector3 e1 = v2.pos - v0.pos;
Vector3 faceNormal = Cross(e0, e1);
//该三角形共享了下面3个顶点,所以将此平面法线与这些顶点的法线相加以求取平均值
mVertices[i0].normal += faceNormal;
mVertices[i1].normal += faceNormal;
mVertices[i2].normal += faceNormal;
}
//对于每个顶点v来说,由于我们已经对所有共享顶点v的三角形 平面法线进行求和,
//因此现在只需要进行规范化处理即可
for(UINT i = 0; i < mNumVertices; ++i)
mVertices[i].normal = Normalize(&mVertices[i].normal);
8.2.2变换法向量
1.切向量u=v1-v0正交于法向量n,如果对此应用一个非等比缩放变换A,则变换后的uA=v1A-v0A没能继续保持与变换后法向量nA的正交关系,因此
通过B=(A-1)T(矩阵A的逆转置矩阵)对法向量进行变换后,即可使它垂直与矩阵经A变换后的切向量uA。
2.当需要对经过非等比变换或剪切变换后的法向量进行变换时,则可使用逆转置矩阵。
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 XMMatrixTranspos(XMMatrixInverse(&det,A));
}
3.再通过逆转置矩阵对向量进行变换时,可以将向量变换矩阵中与平行操作有关的项清零,而只允许点类才有平移变换。将向量的第4个分量设置为w=0,就可以防止向量因平移操作而受到影响,因此就无需再为矩阵中的平移项设置零。但是如果再连接逆转置矩阵以及另一个不含非等比缩放的矩阵,如观察矩阵(A-1)TV,那么(A-1)T中经转置后的第4列平移项将进入最终的乘积矩阵而导致错误的计算结果,因此对矩阵中的平移项设置零是这个错误的预防措施。而法线变换所采用的正确公式是((AV)-1)T
A = [ 1 0 0 0 0 0.5 0 0 0 0 0.5 0 1 1 1 1 ] A=\begin{bmatrix} 1 & 0 & 0 &0\\ 0 & 0.5 &0&0 \\ 0 & 0 & 0.5 &0 \\ 1 & 1 & 1 &1 \end{bmatrix} A=⎣⎢⎢⎡100100.501000.510001⎦⎥⎥⎤
( A − 1 ) T = [ 1 0 0 − 1 0 2 0 − 2 0 0 2 − 2 0 0 0 1 ] (A^{-1})^T=\begin{bmatrix} 1 & 0 & 0 &-1\\ 0 & 2 &0&-2\\ 0 & 0 & 2 &-2\\ 0 & 0& 0 &1 \end{bmatrix} (A−1)T=⎣⎢⎢⎡100002000020