为实现更逼真的3D效果,我们需要光照和阴影
为了理解光照,下面从数学方面来讲(Phong光照模型,参考书籍3d programming game with dx 11)
Normal Vector
为了计算光照,必须取得当前的法线向量
在 Δ p0 p1 p2中,定义 u = p1 – p0 v = p2 – p0
那么表面法线为
可如下计算
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); }
然而对于不规则物体或者是多面体来说,每个点的法线都是不一样的
那么法线的平均值N_avg计算取均值即可
在代码中可以一个面的法向量可以求一个三角形的法向量,然后再单位化即可
Transforming Normal Vectors
既然可以求出每个面的法向量,那么如果这个物体经过某种变换即:
原来向量 u·n = 0
变化矩阵 A,变化后
uA
如何求出变化后的法向量?
以上三幅图说明了如何求变换后的法向量
即:B = (A–1)T
那么代码可以这样写
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)); }
LAMBERT’S COSINE LAW
wikipedia
余弦辐射体,也称为朗伯辐射体(Lambert radiator),指的是发光强度的空间分布符合余弦定律的发光体(不论是自发光或是反射光)
简单来说辐射值和consine值相关
L 和 n 是单位向量
DIFFUSE LIGHTING
漫反射光,假设一个点的材质将光线反射情况如下:
50% red light 100% green light 75% blue light
可表示为
假设光线为白光,强度为80%,可表示为
那么漫反射可这样计算
这时缺少辐射因子,由上面可得
那么乘上辐射因子可得漫反射计算公式
AMBIENT LIGHTING
由于在Phong模型中认为环境光是常量,那么
那么漫反射加上环境光现在为
SPECULAR LIGHTING
镜面反射
已知 n,I和ϕmax,求v即可
V的单位向量为
那么又可得
cos (ϕ) = v ∙ r.(r也是单位化的)
那么r可以这样求
r = I - 2 (n · I)n;(n 是单位化的)
为了使反射效果强烈,我们需要在辐射因子的次方值
即
L·n是检查入射光不能从背面射入并反射
总的光照计算公式
漫反射+环境+镜面
几种灯光类型
PARALLEL LIGHTS
平行光,不赘述,这里需要知道方向即可
POINT LIGHTS
光源Q点,到位置P
方向:
需要记住光源点Q
Attenuation
插入一点,光的衰减与距离的二次方成反比
I0 初始光强,d为距离
我们一般将分母化为如下形式,
那么,加上颜色计算
Range
对于点光源来说,我们加上一个range参数
1)为了对某一特定区域使用
2)为了提高shader效率
SPOTLIGHTS
一个聚光灯模型如下
已知 Q maxϕ,d P
那么光线方向可得
那么cosϕ = -L · d
光强因子
加上颜色计算